jeanne’s oca/ocajp 8 java programmer I experiences

Two years ago, I took the OCA/OCAJP 7 Java programmer I exam and wrote about my experiences. I took the exam this time as part of writing the Java OCA 8 Programmer I Study Guide.

What’s new in version 8?

As you can see from the OCA/Java SE 8 Programmer I official exam page, most of the objectives are the same on OCA 7 and OCA 8. There is a mapping by objective title/number on CodeRanch. The new topics were:

  • Running from the command line
  • Compare and contrast the features and components of Java such as: platform independence, object orientation, encapsulation, etc.
  • Wrapper classes
  • Lambdas/predicates
  • Java 8 date/time classes

How did I study?

As I got a 98% on the previous version of the exam, I didn’t really need to study. [edit: I got a 91% of the OCAJP 8 and a perfect score on all the new Java 8 topics]. It was more of review. Plus writing a book on the topic really gets you ready.  I “studied” by doing all of our review and practice exam questions within a week of the test. This also served as a nice sanity check that the questions we wrote prior to taking the beta were decently in sync. (It’s interesting when writing a cert book that you are writing the questions without seeing the exam. This is good as it prevents accidentally mirroring the questions of the moment in the book. As Oracle changes questions over time, it is better to be learning the topics/tricks from a book and improving your skills/test taking ability.)

To learn the Java 8 in the first place, I read two books:

Oracle has some tutorials:

I also wrote a bunch of practice code. And wrote lots of lambda expressions in other languages.

Test Day

  • The exam software claimed that if you pressed the control key, it would cross out an answer so you could remember which ones you eliminated. That’s a good idea. Unfortunately, pressing the control key did absolutely nothing and clicking merely selected an answer I wanted to rule out as correct. I hope they fix this as it is a nice feature.
  • When I took the OCA 7, I had all the time in the world. On this exam, I had enough time to do the questions, but not enough to review them all. The beta gives you just over a minute per question. The real exam gives more time.
  • I went back to my usual exam center. They gave me an “erasable notebook” with 9 pages and an eraser. This meant I could write as much as I wanted. I probably filled about 4 pages as I went. It’s not the same as the paper/pen they used to gie, but is perfectly sufficient.

time warner cable – different person, different problem?

I haven’t a problem that was maddening when dealing with Time Warner since 2010. It’s that time again.

Background

My cable/DVR box has been slowing getting worsening problems for a while:

  1. freezing periodically for a few seconds
  2. saying there was 0% recorded (a reboot solved that, but it happened about once a month)
  3. turning off and not recording with a reboot. then after the reboot would start recording the middle of a show as if it were a long standing agreement

As I had shows I wanted to watch on the DVR, I chose to live with the first two problems. The third one is core functionality of a DVR so had to deal with it and get a different DVR.

Saturday

I went to the Time Warner store. I returned the Scientific American box and was given a “new” Samsung box. And by “new” I mean a box that someone else returned, they presumably tested and then gave to me. Here’s what happened:

  1. I plugged in the box and it told me to call Time Warner to activate. I haven’t had to do that when trading boxes in the past, but ok. That’s reasonable. They activated the box and had me restart the box. I appeared to have TV.
  2. I went to “Free Primetime On Demand” and got a message to call Time Warner. The rep says she needs to check if it is on my plan. It is FREE primetime on demand. I’m pretty sure it is on everyone’s plan. (This is the station for network tv after it airs. Which I wanted to watch because my DVR wasn’t working last week when I tried to record them.) She eventually gets back to me and agrees I should have it. Guess what the solution was? Reboot the box. I imagine she did something at her end as well.
  3. A little later in the day, I found that cable stations would freeze after a few seconds of play. Network stations (over the air equivalents) played just fine. When I called time warner she “runs a signal test” and tells me the problem is with the box and they can send a tech. If the problem is with the box, I can go to the store and trade boxes again. I don’t need to wait for a tech. When I complain I don’t want to wait for a tech, she tells me it won’t come until Tuesday if they mail a box. I don’t want them to mail the box. I then tried rebooting again.
  4. Provisioning – H/E error – We never got to the bottom of this one although it sounds like an issue with the box. (I also had noCP, but that was when I had the box plugged in wrong.) The tech “ran a signal test.” The tech claimed the provisioning error was because it was a used box and it was downloading updates. At 6:30pm, the rep says she will call me back at 8:30pm to follow up.
  5. She calls at just before 11pm. That’s late to be calling someone. What if I had children who were sleeping? At 11pm, she “ran a signal test” and told me the problem was with the box.

Sunday

Back to the Time Warner store. The rep lives near me and asked if there was construction nearby. There was. He then runs a signal test and tells me there is a problem with the signal. As I spoke to several people who did that the previous day who “ran a signal test”, I was suspicious. He shows me a red screen. However, the cable box was in my hand rather than plugged in at home so still suspicious. He tells me that they need to send a tech to check/fix the signal. Sigh. I give up. Fine. I’ll arrange to be home for a tech to come.

Monday

I had an appointment for between 3 and 4pm. That gave me time to work for half a day in the office and telecommute the rest of the day. I got home around 2pm. I have two messages on my cell phone voicemail and one on my home phone. Two messages say that if I don’t pick up, my appointment will be cancelled. The last says my appointment is confirmed and the tech will call before coming up. Weird.

Since I had time, I decided to go buy lunch. I get back at 2:15pm. At 2:21pm, the tech arrives. That’s not between 3 and 4. What if I wasn’t home? Would I have missed my appointment?

The tech said the problem was the box. (No kidding, this was apparent on Saturday.) He installed a new box. A brand new box.  This time a Cisco one. Which requires over 40 minutes of software downloads before it starts. Which their tech had to be here for. So he got paid to watch videos on my couch for almost an hour. This isn’t his fault. The tech did a great job. He was helpful and polite. Just not necessary. I am perfectly capable of plugging in a box and waiting for an hour.

 

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;
    }
}