fixing clickjacking and brute force login for jforum

I’ve been blogging about some of the security fixes we’ve made in the CodeRanch fork of JForum such as XSS with quotes and CSRF. Today it is time to write about Clickjacking and preventing brute force logins.

Clickjacking

Clickjacking is an attack where someone includes your site in transparent frames and the attacker intercepts anything typed in/clicked. We had originally decided not to do anything about ClickJacking because we want our site to be available in frames. For example, DZone serves the main page in a frame when asking people to vote on it.

However, that doesn’t mean that we shouldn’t protect anything. We decided to protect the most critical parts of the site – the admin screens and the login form. That leaves the read only pages (which we don’t mind framed) and editing posts/user info (which is displayed publicly anyway).

The implementation turned out to be simple. We added this header to the relevant pages. (the admin screens are always within a frame on the site and the login screen is when an admin’s session times out).

response.setHeader("X-Frame-Options", "SAMEORIGIN");

It turned out the method only needed to be called from two places: UserAction (for login) and the AdminCommand (for the admin console).

While this isn’t complete protection, it is certainly better than the nothing we had before. And it is easy to add more screens in the future if we need to. We also didn’t include the JavaScript framebusting logic for older browsers because Google Analytics says hardly of our users are on such browsers.

Preventing Brute Force Logins

Another security issue that we knew about but didn’t fix because “it wasn’t a problem” was preventing brute force logins. It seemed like high time to do something about that one too. While this was more work than fixing Clickjacking, it wasn’t a huge deal either. The logic is mostly in one (well unit tested) class that gets called from the method with the login check. There is also a property file to externalize the number of attempts/wait. And a job to clear out the Map once a day for attempts where the IP gives up and goes away.

The idea is that an IP gets a number of “free” attempts. After that more login attempts from that IP must wait longer and longer. Once a user logs in successfully from that IP or there are no attempts for a certain amount of time,t he clock starts over and the “free” attempts are back.

This approach doesn’t protect against a distributed attack because the IPs are different. (I choose the IP based approach so someone can’t lock out a user’s account specifically by targeting them and entering invalid passwords. That said, we aren’t running a bank so it seems unlikely for someone to go through that much effort to attack us.

Note: I got a private comment to be careful about IP blocking due to companies using a single public IP for thousands of users. This approach is NOT using IP blocking. Let’s say have 1000 users from InfoSys. (We probably have more, but that’s not the point.) Most of those users will either know their password or use the “remember password” functionality. If a few forget their password and type in a bunch of wrong attempts, the time throttling kicks in.  It seems unlikely for multiple users to having this problem at the same time. And as soon as anyone from the company (IP) has a correct password, the “penalty” count resets.

Code for the curious:

package net.jforum.repository;

import java.util.*;

import net.jforum.util.preferences.*;

/**
 * We prevent brute force login attacks by throttling how often we will check
 * credential after three failed logins from the same IP since the last success.
 * If there are no failures within an hour, the clock will restart on this
 * throttling.
 *
 * @author Jeanne
 * @version $Id: $
 */
public class FailedLoginRepository {
    private static final int NUM_SECONDS_IN_MINUTE = 1000 * 60;
    private static FailedLoginRepository cache = new FailedLoginRepository();
    private Map<String, List<Date>> failedLogins;
    private int maxLoginsBeforeThrottling;
    private int numMinutesForReset;
    private int[] throttlingDelay;

    // ----------------------------------------------------------------
    public FailedLoginRepository() {
        failedLogins = new HashMap<String, List<Date>>();
        numMinutesForReset = SystemGlobals.getIntValue(ConfigKeys.BRUTE_FORCE_PREVENTION_NUM_MINUTES_FOR_RESET);
        String waitTimes = SystemGlobals.getValue(ConfigKeys.BRUTE_FORCE_PREVENTION_WAIT_TIMES);
        setWaitsAsIntArray(waitTimes);
        setMaxLoginsBeforeThrottling();
    }

    private void setMaxLoginsBeforeThrottling() {
        for (int i = 0; i < throttlingDelay.length; i++) {
            if (throttlingDelay[i] == 0) {
                maxLoginsBeforeThrottling++;
            }
        }
    }

    private void setWaitsAsIntArray(String waitTimes) {
        String[] stringArray = waitTimes.split(",");
        throttlingDelay = new int[stringArray.length];
        for (int i = 0; i < throttlingDelay.length; i++) {
            throttlingDelay[i] = Integer.parseInt(stringArray[i].trim());
        }
    }

    // ----------------------------------------------------------------
    public static FailedLoginRepository getInstance() {
        return cache;
    }

    // ----------------------------------------------------------------
    /**
     * After a successful login from the API, remove the blocks. This indicates
     * the user just mistyped and not a hacker.
     *
     * @param ip
     */
    public void successfulAttempt(String ip) {
        failedLogins.remove(ip);
    }

    // ----------------------------------------------------------------
    /**
     * Keep track of time of login failure
     *
     * @param ip
     * @param now
     */
    public void failedAttempt(String ip, Date now) {
        removeOldAttempts(ip);
        List<Date> fails = failedLogins.get(ip);
        if (fails == null) {
            fails = new ArrayList<Date>();
            failedLogins.put(ip, fails);
        }
        fails.add(now);
    }

    public void failedAttempt(String ip) {
        failedAttempt(ip, new Date());
    }

    // ----------------------------------------------------------------
    /**
     * If there haven't been any failed attempts from that IP in the last hour,
     * start over.
     *
     * @param ip
     */
    private void removeOldAttempts(String ip) {
        List<Date> fails = failedLogins.get(ip);
        if (fails != null) {
            Date lastFail = getLastFail(fails);
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.MINUTE, -numMinutesForReset);
            if (lastFail.before(cal.getTime())) {
                failedLogins.remove(ip);
            }
        }
    }

    private Date getLastFail(List<Date> fails) {
        Date lastFail = fails.get(fails.size() - 1);
        return lastFail;
    }

    public void removeAllOldAttempts() {
        for (String ip : failedLogins.keySet()) {
            removeOldAttempts(ip);
        }
    }

    // ----------------------------------------------------------------
    public boolean shouldCheckPassword(String ip) {
        removeOldAttempts(ip);
        int waitRemaining = numberMinutesUntilNextAllowedLogin(ip, new Date());
        return waitRemaining == 0;
    }

    public boolean shouldCheckPassword(String ip, Date now) {
        removeOldAttempts(ip);
        List<Date> fails = failedLogins.get(ip);
        return fails == null || fails.size() <= maxLoginsBeforeThrottling;
    }

    // ----------------------------------------------------------------
    public int numberMinutesUntilNextAllowedLogin(String ip) {
        return numberMinutesUntilNextAllowedLogin(ip, new Date());
    }

    /**
     * Contains algorithm for delays: see if already waited number of minutes
     * need to wai
     *
     * @param ip
     * @return
     */
    public int numberMinutesUntilNextAllowedLogin(String ip, Date now) {
        List<Date> fails = failedLogins.get(ip);
        if (fails == null) {
            return 0;
        }
        int index = fails.size() - 1;
        if (index >= throttlingDelay.length) {
            index = throttlingDelay.length - 1;
        }
        int delay = throttlingDelay[index];
        Date lastFailure = getLastFail(fails);
        int numMinutesWaited = (int) (now.getTime() - lastFailure.getTime()) / NUM_SECONDS_IN_MINUTE;
        int result = delay - numMinutesWaited;
        // if delay is over, do need to wait longer
        if (result < 0) {
            result = 0;
        }
        return result;
    }
}

 

Announcing the OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide

Jeanne and I are pleased to announce the upcoming release of our Oracle Certified Associate Java 8 Study Guide for the Java SE 8 Oracle Programmer I exam! We have been working with Wiley Publishing for the past year to bring this book to light, and are thrilled to announce it is nearing completion. Our goal was create a book that is engaging and fun for new and veteran developers alike. The book covers all of the new features that you are required to know for the OCA 8 exam including: the Local Date/Time API, the Period class, lambda expressions, and more. And of course, it covers the “old” topics as well.

scott-and-jeanne
The book, to be tentatively titled OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide: Exam 1Z1-808, will be available for purchase later this year on Amazon.com and wherever books are sold.

Oracle has already published a beta version of the Java SE 8 Programmer I exam, with the official version to be available in the coming months.

Please visit our new OCA: Oracle Certified Associate Java SE 8 Programmer I Study Guide page for the latest information about the book. If you have any questions for us, feel free to post in the OCA forum at CodeRanch, which Jeanne and Scott visit frequently.

own your expertise – a conference for women developers

Yesterday, <Write/Speak/Code> had a one day workshop for women developers called “Own your Expertise.” The idea is for more women to start “Building the foundation for  Thought Leadership, Conference Speaking & Open Source Contributing.” I had mixed feelings about going to a female only event. I asked a teammate to join me though and off we went. In this blog post, I’ll share some of the “gender” topics that came up and my reaction to them. [The rest of the workshop was more content driven like brainstorming topics].

Networking

There was lots of time for networking. Some of it was just general time. Some was structured networking.

My reaction: The environment was set up well for people to talk to each other and share experiences. Unlike at a core-tech conference, there were more icebreakers and “natural” opportunities to talk to people. I also felt like there were less people on laptops all day (presumably because it was Saturday) or with a group of colleagues that all came together. This made it easier to approach people. Also, there was a good mix of employed people, students and those looking for jobs. There were also a lot more career changers than at the typical tech event I go to.

Impostor Syndrome

  • Women have more trouble internalizing accomplishments
  • Proof of success is dismissed as luck/timing
  • This is common even in high achieving women
  • Even as kids, guys tend to attribute failure to luck or bad judging
  • Women tend to underestimate abilities.

My reactions: 

  1. I was surprised at the number of hands that went up when asked if the audience was not sure if they were as good a developer because they didn’t start coding when they were 12 or code for fun. (I do code for fun and started when I was a kid.) It’s ok not to code for fun. That said, practice makes perfect. And I believe those who code for fun are likely to become better developers and faster.
  2. A lot of this resonated from the book “The Confidence Code“. Which helped me realize that guys are more likely to sound confident even when wrong. Which is a problem because it encourages management to see that “the guys know what they are doing.” Making this a self fulling prophecy.
  3. Personally, I saw this last week when I gave a presentation. I’m not normally nervous when giving a presentation. For this one I was because my exposure to the topic was one day of a vendor showing us the product. I was fine. I knew almost everything I was asked. At least I didn’t APPEAR nervous.
  4. I also see see this when guys sound more confident even when wrong. I feel a resistance to challenging someone who sounds 100% confidence because it makes me doubt whether I’m right. I’ll check after the meeting, but sometimes the moment is gone then.

Stereotype Threat

People tend to perform worse when worried about refuting a stereotype

My reaction: I agree with this. It becomes a distraction. Or you feel like you have to do more because of it. Luckily, I’m good at not noticing. Every once in a while, someone will point it out and then I’ll notice. For example, there was one point where I was the only female under my officer’s part of the company. (I work for a bank; our senior managers are called officers.) This meant I went to a meeting every few months with all guys. I hadn’t noticed until someone started talking about the topic. Then I had an “oh, yeah” moment. Luckily, I do believe that the guys didn’t care either and moved on quickly. But it was certainly a distraction for the day or so that it was on my mind.

Negative feedback loop

The work too hard loop:

worry –> work hard –> good job –> approval/relief –> worry

My reaction: While I didn’t experience this one personally, I have seen it in a few others. Of both genders though. It does highlight an important lesson about not worrying (excessively) about things you can’t control.

Objective feedback/support system

  • Have people you can talk to who you trust to be objective
  • What’s the problem? What’s the worst that can happen? Is what you’re feeling objectively true or just your perspective?

My reaction: This is definitely important. Since we were in a workshop that frequently brought up gender, I ran through a tally of the genders of the people I talk to when having difficulties in a tech environment.

  • At CodeRanch, it is 3 men and 1 woman. (Most of the senior moderators are guys so I suspect this is talking to who is available.) At CodeRanch, most of the “problems” are someone saying something in the forums that is mean/hurtful. But sometimes, I’ll ask these people for advice on how to deal with an anonymized work problem if the people I’d ask at work are too close to it.
  • At work, it is 4 women and 1 man. Interesting how this is the reverse. Granted, I talk to other people about problems (like my manager at the current time.). I’m only counting the people that I feel like I can say anything to. While I’ve come close to that point with managers, I don’t think I could get 100% there.

Qualifying accomplishments

  • Women tend to use words that downplay what they do. Words like “just”, “only”, “good timing”, “luck”, etc make the person sound less skilled.
  • The idea is not to brag, but then it gets over done.

My reaction: They did an exercise where you were given an accomplishment and just said “thank you.” That was really easy. While I do use unnecessary qualifiers, I don’t do it a lot. And I don’t tend to do so in response to a compliment. I think when I use the qualifiers, I’m thinking about precision or not taking credit for something others do. That said, this is something I definitely see when talking to people. “I just figured out …”

Look at the data

Record positive feedback, measurable progress, accomplishments.

My reaction: Right on! At my first job, I was given the advice to record what I accomplish throughout the year so I remember it when it is appraisal time. This is excellent advice. I have a document at work with just that. I also have a list of public (non-work) accomplishments on my CodeRanch bio. It contains certifications, public conference talks, book contributions/tech editing, robotics and CodeRanch. I started posting this list to establish tech credibility. Then I continued because it is cool! I like that it is on CodeRanch rather than Linked In. This makes it about sharing rather than looking for a job.

“You guys”

The presenter said “you guys” once and  corrected it to “you ladies”

My reactions:

  1. I use the phrase “use guys” all the time. I consider it a gender neutral phrase. The problem is that “girls” maps to “boys” and “ladies” maps to “gentleman.” Girls is no good because it implies children. And ladies seems oddly formal.
  2. I don’t mind if someone uses an actual male phrase (online or at work) as long as it doesn’t keep happening. At CodeRanch, it is because Jeanne isn’t recognized as a female name internationally. I’ve corrected “sir” a number of times online.

Your body language shapes who you are

We saw a snippet of this Ted talk. The major themes in the snippet we watched were:

  1. you should just do something and eventually you’ll become it
  2. power poses increase confidence (and guys take up more space)

My reaction: The Power Pose thing felt stupid. Not passing judgement on it though. New things sometimes to feel stupid/silly. Google has millions of hits on it including yoga, so there is presumably something to it.

Credibility

  • Knowledge
  • Expertise – There was an exercise where everyone said “I’m an expert at ______ because ________”.
  • Shiny bauble – you have a small amount of time to convince others they should listen to you. Grab attention

My reaction: This was easy for me because I’ve done a lot :). It was interesting to see what others said. I was also in the same group as my teammate for the expertise exercise. It was nice to hear her say her expertise confidently.

Help others

  • Teach things that you already know
  • Ask the first question so someone else doesn’t have to
  • Connect with others
  • Reframe bragging into sharing and helping others

My reaction: Funny, I do ask the first question a lot. I agree with the helping others. That’s a part of robotics mentoring and CodeRanch and Toastmasters. All hobbies of mine. While I don’t like “be a good female role model”, I don’t think about that much. Most of the time, I’m me and I happen to be female. Now let’s go talk about tech!