Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Identifying clients

by ruzam (Curate)
on Dec 06, 2006 at 20:16 UTC ( [id://588176]=perlquestion: print w/replies, xml ) Need Help??

ruzam has asked for the wisdom of the Perl Monks concerning the following question:

Well, shamefully I've searched this topic and found answers that say it can't be done. I'm going to ask the question again, knowing I'll get the same answer, but expecting something different. I'm sure I read somewhere that this is a definition of insanity, but here goes anyway...

I have a web based application (written in perl of course). It uses a simple user login scheme to generate a unique session id which is used by the client until the user logs out (or the session times out). Nothing special there.

If a client attempts to run the application with an unknown or expired session id, the client ip is placed a penalty box for a timeout (no login allowed during the timeout). If the client attempts to login with an invalid password (no session id), the client ip is placed in a penalty box for a timeout. If the same client ip attempts to run the application during the timeout, not only do they get refused, but the timeout gets extended, exponentionally (4 seconds, then 8 seconds, 16, 32, 64, etc). This appeared to be reasonable approach to prevent brute force attacks of either the login/password or session ids and works very well when each client has a unique ip.

Recently this approach hit a little snag. A number of the users were sitting behind a nat router and thus appeared to the application as the same ip (many of you are probably already chuckling). Things worked fine until one of the user session ids expired. When this client tried the application (after expiry), the ip was sent to the penalty box - 4 seconds. At the same time other users continued to use the application (with their perfectly good session ids), but the ip was now blocked, so the application dutifully refused each user request and upped the timeout with each request as well. More users found the application wasn't working and before you know it, that particular ip (and all the users behind the router) were blacklisted for 6 hours and growing.

This scenario is likely to be common (and unacceptable) for the application. I've wracked my brains and searched till my fingers bled, but I just can't see the logic to get out of this. The users are trusted and co-operative, so if there's a way they can set something in their browser to distinguish themselves (they're nearly all running personal laptops) then this could be used as a solution (blacklist ip plus some other client provided data). Not perfect, and not 100% secure, but reasonable for the usage.

Perhaps a cookie intervention might save the day? If I tie a random value to a session id and save it with a client cookie is that any more secure? (I can require users to have cookies enabled). Or is a cookie no different than the original session id? What's to stop an attacker from forging up cookies?

My worst case option at the moment is to have an ip whitelist built into the application to not block attempts from particular ips (trusted router networks). I'd like to avoid this if possible.

Any sane or insane ideas on how to handle this problem? I don't need a department of defense strength solution here!

Replies are listed 'Best First'.
Re: Identifying clients
by gaal (Parson) on Dec 06, 2006 at 20:23 UTC
    If a session already has valid creds, simply don't penalize it. You can still penalize new logins from that IP if you really want to, but I wouldn't recommend it, because that way anyone who can spoof bad logins from that IP can DOS the service at little effort.
      I considered checking the session id first and skipping the ip check if it was valid. But if I were to do that, then there's really no point to checking ips at all. An attacker could simply scream through session ids until an active one was found.
        I make my session IDs 64 bits long. Even if they could check 1 million/sec, it would still take more than 5000 years before they could have even a 1% chance of finding one.
        Why not store the IP address in the session id as well. Even if some were to brute force a session ID they would also have to know the IP address being used by that particular session. That would be harder to crack than just a session ID.
Re: Identifying clients
by Firefly258 (Beadle) on Dec 07, 2006 at 02:16 UTC
    A number of the users were sitting behind a nat router and thus appeared to the application as the same ip (many of you are probably already chuckling).

    It's not a laughable mistake, it has been made before. This form of NAT (Overloading or PAT - Port Address Translation) has been around for quite sometime now, I don't see why this potential snag was picked up much earlier (during the design phase).

    Even though multiple clients behind a Port Translating Router appear to have the same IP address, each HTTP connection is established from a unique source port on the PAT router and perhaps this could be used to your advantage. Maybe it helps to put an IP address + Port Number combination like 172.16.0.1:2048 (Or, even better, if you are using IO::Socket (or similar), the socket structure) in the penalty box?

    On the other hand, maybe that isn't such a good idea. Not all browsers reuse an existing source port (and therefore the router doesn't necessarily use the same ports either) especially if the server doesn't send out/doesn't need to send out frequent keep-alives.

    Using a hash taken from multiple values such as client's IP address, initial port number, client's local time, client's user name, a new value from your ID pool, the server's local time, etc stored in a session cookie seems like a good solution. The more context-sensitive values you use, the harder it is to determine the session ID, especially if your session-ID algorithm is unknown to the users and the hash is a secure, tested one-way digest like SHA1 or SHA256.

    Having said that, It is still not completely impossible to determine and forge session-IDs but if a session ID is generated anew after a user is authenticated (via password or other means) a hacker really has little chance in getting aroud session IDs. A hacker might compromise a user's password and bypass session ID security, but that is a whole different issue. Yes, you really ought to use session cookies if you wish this information to persist.

    Session IDs should not really be considered a security measure, only as a means for identifying a particular session - DO NOT use the session ID as a form of authentication under any circumstances, you must always rely on the user's credentials for that.

    update: My 2 cents on what constitutes a session after Washizu pointed out the potentially user-frustrating timeout problem.

    If you are really worried about security and your application demands it, HTTPS addresses your concerns.


    perl -e '$,=$",$_=(split/\W/,$^X)[y[eval]]]+--$_],print+just,another,split,hack'er
Re: Identifying clients
by serf (Chaplain) on Dec 07, 2006 at 10:58 UTC

    Hi ruzam,

    In Re^6: Identifying clients you asked "So is my only option to literally forget the whole ip blocking strategy?" - I think the answer to this is yes.

    If your application is trying to protect against unauthorised access from Internet users (as opposed to Intranet access) then the client IP is not a valid criteria to identify them by anyway.

    At the moment I accessing the Net via tor. Every time I hit a page my request arrives at the server via a different route.

    You can check this by installing Tor & going to a site like http://www.whatismyip.com/ - each time your IP will be different.

    This has interesting effects when for example go to Google - instead of bringing up my normal google.co.uk start page, it bounces me to the country that it thinks I'm coming from.

    Yesterday I got google.de a number of times, and once I even got the Bulgarian Google homepage - "What's this??? Google in Cyrillic!" :o)

    DigitalKitty has a bit more on tor on her home node.

    If someone is using tor then they have immediately rendered all your hard work and logic for blocking/penalising IPs irrelevant.

    A white list *is* a possiblity, but even valid IPs can be hijacked or spoofed, so it's never 100% reliable as an identification method.

    I would think a form of challenge-response type encrypted token passed between the server and the client and back to the server would be a more reliable way to go.

Re: Identifying clients
by leighsharpe (Monk) on Dec 06, 2006 at 23:19 UTC
    What about something like:
    if (session id present && session id valid) { Let them in, irrespective of whether their IP is in the penalt +y box } elsif (session id present && session id invalid) { Penalize them } elsif (ip address in penalty box) { Penalize them further # (No session id present and they're alre +ady in the bad books.) } else { Check their username/password details and penalize them if necess +ary. # No session ID, not currently in the penalty box. }
    I think this covers all scenarios.
Re: Identifying clients
by Washizu (Scribe) on Dec 07, 2006 at 03:38 UTC

    This is slightly off topic for the question, but this line got me thinking.

    "If a client attempts to run the application with an unknown or expired session id, the client ip is placed a penalty box for a timeout (no login allowed during the timeout)."

    A good example of a harmless visit to the penalty box is the user who is using the app and goes off to lunch. He returns to his browser and tries to use the app, sending his ip to the box because of an expired session. He refreshes or hits the back button, sending him to the box for 8 more seconds or maybe more depending on how many times he tries to use it. Why not just send the expired users straight to the login screen with no penalty at first (maybe a zero second penalty) and then see what they do? Just a suggestion.

    -----------------------------------
    Washizu
    Odd Man In: Guns and Game Theory

      The algorithm ought to treat unknown or expired session IDs as invalid and proceed to authenticate the user before generating a new, unique, valid session ID for the new session. This way there'd be no need to involve a potentially irritating time-out problem. A timeout should probably be used after repetetive failures to authenticate the user properly.

      Also, by the very definition of a session, a session ought to expire after a certain period of inactivity, otherwise one could just do away with the need for sessions IDs to identify a "session" as a period of continuous involvement with the application and we could term the ID as a client ID instead as there'd be little or no variance.


      perl -e '$,=$",$_=(split/\W/,$^X)[y[eval]]]+--$_],print+just,another,split,hack'er
        The flaw (or feature) in the current design is that the session entries are cleared as soon as they expire. So if a user tries to continue with an expired session id, the application has no reference to be able to say if the session is simply expired or it's a malicious attempt to steal someone's session. That's the line of thinking that started the whole ip blocking plan.

        Since I can't rely on the ip I don't think there's anything I can do to prevent a session attack so there's not much point blocking or timing anything ip or session related.

        About the only thing I think I can do at this point is penalty box the user id after a failed login. At least then I can slow down a password attack, while not affecting an already logged in user.
      That's exactly what triggered the problem. The first penalty is a mere 4 seconds (barely time for the user to read it and realize the session timed out), and always redirects back to the login page. It's the repeated failures that grow the timeout, and with many users under the same IP, they all get hit regardless of session status.

      Determining a first time session expire (vs repeated session attempts) isn't built into the design yet. It's a work in progress...
Re: Identifying clients
by themage (Friar) on Dec 07, 2006 at 10:35 UTC
    Hi ruzam,

    The way I usually handle this is by creating a MD5 of several information, including the client IP and a secret passphrase (that can be fixed - always the same, or changed at intervals, in which case all sessions would expire in the end of the period), and using this MD5 as sessionID.

    This allows me to verify that the sessionID was generated for the specific IP (and maybe UserAgent, if you use this to create the MD5), and the passphrase makes it harder to generate a valid MD5 key.

    To limit the number of connections per IP to a legitime limit I would use something as the Apache::SpeedLimit example given in the Writing Apache Modules book. This would prevent a bruteforce attack to the sessionID.

    TheMage
    Talking Web
Wrong layer
by snowhare (Friar) on Dec 07, 2006 at 14:16 UTC

    Use SSL/TLS and use client certificates. Issue new users a cert with installation instructions for their web browsers and your web server can be configured to identify them for you. More - you can even lose the need to explicitly 'login' if you want - it can be handled by the SSL/TLS handshake. If you don't want to fork out the money for certs from a retail cert authority, use openssl to generate your own 'CA' and certificates for free.

    A little Googling will tell you how.

      A problem with this strategy is that a client is restricted from using your service from a single machine and a single user account on that machine unless user profiles are truly distributable across many machines, but again the access is limited to those infrastructures that maintain roaming profiles. It nevertheless is the most secure strategy but at a cost that makes is quite infeasible to deploy in the real world.


      perl -e '$,=$",$_=(split/\W/,$^X)[y[eval]]]+--$_],print+just,another,split,hack'er
Re: Identifying clients
by Anonymous Monk on Dec 07, 2006 at 10:02 UTC
    Consider not blocking ip addresses on the first incorrect access wait until there are, say, ten incorrect accesses in a minute.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://588176]
Approved by Joost
Front-paged by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2024-07-14 02:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.