
One Line of Code That Compromises Your Server - joeyespo
https://martinfowler.com/articles/session-secret.html
======
parent5446
This is why I prefer not to pass session information to the client in the
first place. Have the session key be just an opaque, cryptographically random
token that is then associated with data on the server (via a keystore of some
sort). Then the cookie becomes nothing more than a bearer token.

There are some use cases this does not cover, such as authentication across
multiple systems, where one or more may not have access to the authentication
source, but even in that case it'd be preferable to have a separate, internal,
private microservice that exchanges tokens for session information.

None of this will stop an attacker that has network access, but that's not
what the article is considering, and if somebody is inside your network you
might have bigger problems than session hijacking.

~~~
fpgaminer
The reason is that, at scale, all the little things matter. Having to query
your database for _every_ user request, to convert their session token into
the server-side underlying data, gets expensive and slow. And it's a
centralizing force in the architecture, requiring your session management
database to be accessible by all your user facing servers.

So they store the data with the client, who can pass it back to the server
with every request. One less DB query.

There are various rebuttals to that approach, even at scale, etc, etc. I'm
certainly not advocating for it. But that's what some big applications landed
on. Big apps drive most of the development of free tools, libraries, and
frameworks. So it's no surprise that a lot of free tools handle this use case,
even by default.

In the majority of cases, the not at scale cases, of course it doesn't make
sense. Most all the hot new tools don't make sense. But again, that's just an
artifact of software economics. The majority of the money is made by the big
apps, so most tools are built for the big apps.

It's just a shame that new developers get caught up in these hip tools and
base their understanding of the ecosystem on them.

~~~
hdhzy
> One less DB query.

This trait constantly appears in threads about JWT. But then how do you handle
token invalidation?

I wonder how big IT companies (Google, Microsoft, Amazon) are really doing
it...

~~~
jsjohnst
Most of the big companies to my knowledge generally don't depend on client
side storage of session state (they might in a few isolated cases, but in
general avoid it).

~~~
hdhzy
Thanks.

Google uses interesting approach to handling service accounts: JWT is used as
user credential once to get the access token (session credentials) that is
reused between requests.

[https://developers.google.com/identity/protocols/OAuth2Servi...](https://developers.google.com/identity/protocols/OAuth2ServiceAccount)

------
franciscop
Good article and callout for those projects (and mine). I am doing an express
alternative[1] with something that is not secure as a temporary session
secret[2] formed with `secret: 'secret-' \+ parseInt(1000000 * Math.random())`
so I'll modify it to use a real random key from a crypto library or display a
prominent warning. Then I am also documenting it in a small _production
security_ guide [3]. Also, if the key in the documentation is used there's a
warning[4] similar to how Dropbox stops you from using 'correct-horse-battery-
stapler' as a password.

If you have any other tip/recommendation I'd be really thankful.

BTW, doesn't SSL/TLS mitigate this a lot? Like, if the user is using SSL then
only with physical access to a decrypted device would an attacker be able to
impersonate the user, limiting greatly the possible attacks. And if someone
has access to your decrypted device then you have bigger problems. Though devs
who don't change 'super secret' are probably probably less likely to set up
https...

Edit: I almost didn't include the _BTW_ comment because I agree that it _is_
an important part of security, so I didn't want to make it sound less severe,
but I am curious about attack possibilities with https enabled.

[1] [https://serverjs.io/](https://serverjs.io/)

[2]
[https://github.com/franciscop/server/blob/master/src/config/...](https://github.com/franciscop/server/blob/master/src/config/defaults.js#L8)

[3]
[https://github.com/franciscop/server/issues/3](https://github.com/franciscop/server/issues/3)

[4]
[https://github.com/franciscop/server/blob/master/src/config/...](https://github.com/franciscop/server/blob/master/src/config/index.js#L45)

~~~
AgentME
SSL doesn't protect you from a malicious user who is trying to crack the key
used on the cookie you gave him. It just means that no one else will eavesdrop
on the malicious user's connection.

~~~
franciscop
But then it also means the malicious user cannot eavesdrop on other users, so
other users will be secure, right?

~~~
AgentME
The attack in the article isn't about a malicious user eavesdropping and
obtaining someone else's cookie directly. It's about a user who gets a cookie
from the server, brute-forces to determine the key used to sign the cookie,
and then uses that key to generate cookies that authenticate them as other
users (or that execute code during deserialization, etc).

~~~
jacksingleton
@AgentME -- correct

@franciscop -- keep an eye out for the rest of the article (it's already
written and will be released soon). I go over the impacts of an attacker
knowing the secret in much more detail. I also have sections on prevention for
both application developers and library/framework authors that I think you'll
find interesting!

~~~
franciscop
I'll wait for it, because I don't fully understand @AgentME's comment "to
generate cookies that authenticate them as other users". Those auth cookies
would have a hashed token per user that must be created and validated in the
back-end (besides the encryption). So just storing 'user-ID' _should not_ be
enough, you'd have to be able to decrypt a real user's cookie to know this
token hash (and for this reason just guessing numbers/users ID would not be
enough as well).

~~~
AgentME
The cookie that the user gets might be something like userid + ":" \+
HMAC(server secret, userid). The user who gets that cookie can brute-force
HMAC(x, userid) with different values of x until they get a match for the
string in their cookie, at which point they know the server secret. Then with
the server secret, they can generate a valid cookie for any userid.

~~~
franciscop
That is exactly what I mean, you are _not_ supposed to save the user id
(hashed, encrypted or otherwise) in the cookie. You are supposed to save a
random secure token that will be associated to that user's device/login. If
you save the user id it'd be as an index, not for auth as the token is for
that. See
[http://stackoverflow.com/a/32218069/938236](http://stackoverflow.com/a/32218069/938236)

Of course then it depends on the developer, so statistically speaking there
will be a % who do what you suggested and making it secure from the
library/framework side would help some users, so I'm all in for it.

~~~
AgentME
That strategy is often called sessions or a stateful-cookie. It requires all
of the servers that accept that cookie to be able to share their session state
(or for a strategy like sticky sessions to be used). The strategy I described
is stateless: the servers only need to share the secret in order to verify the
cookie. It's a popular strategy but it does have some trade-offs, such as
being vulnerable to anyone who knows the secret.

------
bshimmin
_Jack Singleton is a developer and security specialist at ThoughtWorks._

What a splendid example of an aptronym! [0]

[0]
[https://en.wikipedia.org/wiki/Aptronym](https://en.wikipedia.org/wiki/Aptronym)

~~~
ams6110
Max Venturi, a test driver at Ferrari, is my favorite one.

~~~
mwpmaybe
Mine is Staff Sergeant Max Fightmaster, US Army.

I'm also found of Rick Wagoner, former CEO of GM, and I often daydream of US
Senator Sheldon Whitehouse becoming President.

~~~
clappski
Richie Rich and Bob Diamond - Bank executives is my favourite.

~~~
dcwca
Rich Fairbank, CEO of Capital One. Doesn't get much better than that, folks.

------
mozumder
The cookies spec really should be incorporated into the SSL/TLS layer. Right
now it's basically performing two encryption/signing steps immediately after
each other, one at the web server, and another for the applications server.
This is a pain-in-the-ass for the web server framework and adds several
milliseconds of redundant latency.

The cookie data should be signed with the web server's private key.

~~~
hdhzy
Token Binding achieves something simular to what you propose. See
[http://www.browserauth.net/token-binding](http://www.browserauth.net/token-
binding)

------
bitexploder
While we are at it here is another individual line that will "compromise your
server".

[https://github.com/rack/rack/blob/master/lib/rack/session/co...](https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb#L180)

[https://rdist.root.org/2009/05/28/timing-attack-in-google-
ke...](https://rdist.root.org/2009/05/28/timing-attack-in-google-keyczar-
library/amp/)

Of course folks are much more likely to misuse their cookie secret than
purposefully break a library function. That said you can look at the history
of most projects that use HMAC to authenticate data and they did it wrong.

(e.g.
[https://github.com/rack/rack/commit/0cd7e9aa397f8ebb3b8481d6...](https://github.com/rack/rack/commit/0cd7e9aa397f8ebb3b8481d67dbac8b4863a7f07))

------
eleumik
What do people here use to store password for servers ? Plain text
configuration files ? I have pwd of DB in config files and found no way to put
it elsewhere.

~~~
mkarazin
Plain text can work, assuming that it is access controlled in a secure
environment. Often times, during the build process, a secure secret management
system or process will handle the combining of the code with configuration.
For example, your developer (with no secret access) could commit code and
Jenkins can fetch the configuration file from a secure and access-controlled
service. Then the built code has the secrets and the developer does not. There
are a lot of approaches to doing it at scale. See
[https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3...](https://gist.github.com/maxvt/bb49a6c7243163b8120625fc8ae3f3cd)
for a good overview.

~~~
eleumik
Thanks ! very nice overview.

------
cyberferret
I write most of my web apps using Sinatra (as per the article example), but do
it using the Padrino [1] framework. Padrino by default sets the Sinatra
session secrets to a long hash, which negates the developer from having to
remember to do all this housekeeping later to tighten security on the app.

[1] - [https://www.padrinorb.com](https://www.padrinorb.com)

~~~
arkadiyt
Padrino sets the session secret to a random value on startup if you don't set
it yourself. This means that:

1) you can only run a single instance. Multiple instances will generate
different secrets and not accept each others' cookies

2) restarting an instance (for instance doing a deploy) generates a new secret
and invalidates all previous cookies

Neither of these are desirable, and to avoid it the docs recommend [1] you set
the session secret yourself via:

    
    
        set :session_secret, 'super secret'
    

which gives you the exact problem the blog post is addressing, so it does not
seem that Padrino does any better here.

[1] -
[http://padrinorb.com/guides/controllers/sessions/](http://padrinorb.com/guides/controllers/sessions/)

------
nthcolumn
I don't know why anyone would ever do this since there is no cost to
SECRET_KEY = os.urandom... or even just hammering on the keyboard. It is not
like you are ever going to be challenged for that password? You could even say
here is another line of code that will compromise your server:

rm -rf /*

brb off to search github for SECRET_KEY =

~~~
jjnoakes
> there is no cost to SECRET_KEY = os.urandom

Only if you generate the key up front and use the same value everywhere.

If you just call os.urandom in your app, then the cost is every restart (and
every separate server instance) invalidates the cookies.

Probably not what you want.

------
mwpmaybe
How long would it have taken to crack the secret on that same setup if HMAC-
SHA256 had been used instead?

What's the recommended minimum number of bytes for an HMAC secret when using
SHA1? SHA256?

------
kuon
I think any sane framework should generate a random secret each time an app is
generated.

~~~
ameliaquining
No; Django does that, and the result is that people check that random secret
into source control. The correct solution is to not have the secret in code at
all, but to read it at initialization time out of some kind of secrets
management system (traditionally via environment variables). Unfortunately,
frameworks generally can't do this in a hosting-setup-agnostic way; you have
to understand the specific facilities that your PaaS or deployment system
offers for this, and some (looking at you, Google App Engine Standard
Environment) don't offer adequate ones.

~~~
robertlagrant
I THOUGHT Django generated a random secret! What's MF on about?

~~~
jacksingleton
ah I can see how that sidebar is confusing now. the point was just to
highlight the configuration names across different frameworks. as
@ameliaquining points out, a randomly generated secret that's been leaked to
github has much the same effect as one that is easily guessed.

I'll think about ways to make it more clear! Thanks

------
azernik
The equivalent of leaving the default password set. Coders are not immune.

~~~
continuational
Default passwords are a problem with the system being configured; not with the
configuration. Insecure-by-default systems should not be deployed anywhere.

------
tbodt
Compromise your server with this one weird line of code!

------
known
I've seen one character compromising the server

