Giter Site home page Giter Site logo

security-adventure's Introduction

Build Status

This repository contains an exciting quest to learn about Web security by learning about vulnerabilities, exploiting them, and then crafting code to protect against them.

Prerequisites

Before embarking on this adventure, make sure you have the skills taught in learnyounode and levelmeup.

Also, make sure phantomjs is installed and on your path (as well as node 0.10 and npm).

Start The Adventure!

Via npm

Start the adventure like so:

sudo npm install -g security-adventure
security-adventure

That's all there is to it; the security-adventure program will instruct you further.

Via git

Alternatively, you can clone this git repository and follow the instructions in the rest of this README:

git clone https://github.com/toolness/security-adventure.git
cd security-adventure
npm install
cp app-vulnerable.js app.js

app.js is a full web application in about 150 lines of code that allows users to create password-protected accounts and store private plaintext notes in the cloud.

Run node app.js and then browse to http://localhost:3000 to familiarize yourself with the behavior of the application by creating a user and saving some notes. Then read the source of app.js to get a basic idea of how everything works.

app.js contains lots of vulnerabilities, and your quest is to learn about and fix all of them!

Vulnerability: Regular Expression Denial of Service

The regular expression used to validate usernames has a Regular Expression Denial of Service vulnerability in it.

Read about this vulnerability, and then try exploiting it manually by visiting the app in your browser and entering an invalid username that will cause the app to hang.

Then fix app.js.

Run bin/verify.js redos to verify that your solution works.

Vulnerability: Reflected Cross Site Scripting

The home page of the app accepts a msg querystring argument containing a hex-encoded status message to display. This is used, for instance, when users fail to authenticate properly and the server needs to provide feedback.

This isn't exactly a best practice for various reasons, but most importantly, it contains a Reflected Cross Site Scripting vulnerability!

Read about the vulnerability, and then try crafting a URL that, when visited, causes a logged-in user's browser to display an alert dialog that contains their session cookie (accessible through document.cookie).

Stopping Cookie Theft

Cookie theft is a particularly big danger because it allows attackers to do anything on a user's behalf, whenever they want. So first, mitigate the effects of XSS vulnerabilities by modifying sessionCookie.serialize() in app.js to issue HttpOnly cookies.

Manually test your solution by loading your specially crafted URL from the previous section; you shouldn't see the session cookie in that alert dialog anymore (you will have to log out and log back in for the HttpOnly cookie to be set properly).

Run bin/verify.js httponly to verify that your solution works.

Defining a Content Security Policy

It's nice that the damage that can be done via the XSS attack is somewhat mitigated, but it's way better to prevent the attack entirely!

The Content Security Policy specification is one of the most awesome security innovations to come to browsers in recent years. It allows servers to change the default allowances for what kinds of script can be executed, and even what kinds of embedded resources (such as iframes, images, and style sheets) can be included in a page. This is in accordance with the Principle of Least Authority, which is a good best practice for any secure system.

Since our app doesn't actually have any client-side script or embedded content, we can enforce the most restrictive CSP possible by setting the Content-Security-Policy header to default-src 'none'.

Once you've done this, load your specially crafted URL again; you shouldn't even see an alert dialog, and your browser's debugging console might even explain why your JS wasn't executed.

Run bin/verify.js csp to verify that your solution works.

Stopping XSS with HTML Escaping

CSP is only available on the most modern browsers, and we need to protect users on older ones too. Besides that, of course, we actually want to display the message content in a correct and useful way.

This can be done by properly escaping the untrusted input coming in from the msg querystring argument.

The OWASP XSS Prevention Cheat Sheet is indispensable here. Check it out and use a reliable function like underscore's _.escape to escape the msg argument before inserting it into your HTML. (Note that if you decide to use underscore, you'll want to install it first using npm install underscore.)

Run bin/verify.js html-escaping to verify that your solution works.

Vulnerability: Cross-Site Request Forgery

Cookies are a form of ambient authority, which means that they get sent with every request to a website--even when that request comes from a different website!

Consider a website called killyournotes.com which contains the following form:

<body onload="document.forms[0].submit()">
  <form method="POST" action="http://localhost:3000/">
    <input type="hidden" name="notes" value="gotcha.">
  </form>
</body>

Every user logged in to your application would immediately have their notes deleted whenever they visited killyournotes.com!

Try doing this now: copy the above text and paste it into an HTML file anywhere. Then visit the file in your browser and see what happens.

This is called a Cross-Site Request Forgery (CSRF) because it involves another site "forging" a request to your application and taking advantage of the ambient authority provided by cookies. In security parlance, your application has unwittingly become a confused deputy.

This exploit can be protected against by requiring that every incoming request that changes your application's state (e.g. a POST request) also come with an explicit token guaranteeing that the request indeed came from a page on your site, and not someone else's.

To complete this mission, you'll need to do a number of things:

  • When a GET request arrives at your application, check to see if the session has a value called csrfToken. If it doesn't, create one using crypto.randomBytes() and set the session cookie.

  • Whenever your site displays a form, add a hidden input with the name csrfToken to the form, and set its value to that of session.csrfToken.

  • Whenever your site processes a POST request, ensure that the incoming form data has a value for csrfToken that matches that of session.csrfToken. If it doesn't, return a 403 (forbidden) reponse code.

Once you've done this, your exploit should result in a 403 instead of deleting the current user's notes, and your application should still retain all existing functionality.

Run bin/verify.js csrf to verify that your solution works.

Hooray!

You've completed all the challenges so far. You can verify that your app.js protects against all the problems you've solved, and still retains its basic functionality, by running bin/verify.js all.

If you want to learn more about Web security, you should read Michal Zalewski's The Tangled Web. It is hilarious and very educational.

Goals and Future Plans

app-vulnerable.js intentionally contains a number of OWASP-defined security vulnerabilities that aren't currently part of the quest, such as:

Learners should first exploit these vulnerabilities, so they understand how they work, and then modify the code to implement defenses against them.

Ideally, the tutorial will also teach users about more recent innovations in browser security, such as HTTP Strict Transport Security. It should also teach developers how to use security tools like the Zed Attack Proxy to easily detect for vulnerabilities in their own applications.

By the end of the tutorial, users will have familiarized themselves with a variety of types of attacks. The will also have familiarized themselves with the OWASP website and will be equipped to independently learn about security in the future.

security-adventure's People

Contributors

toolness avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

security-adventure's Issues

Can't install - SHASUM error

sudo npm install -g security-adventure

Fails to install with the following error

npm ERR! Darwin 15.0.0
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "-g" "security-adventure"
npm ERR! node v4.2.2
npm ERR! npm  v2.14.7

npm ERR! shasum check failed for /tmp/npm-16745-a28b2b6c/registry.npmjs.org/wd/-/wd-0.1.4.tgz
npm ERR! Expected: 5adcf7265153edbf46abccb3e82de4c8696a344f
npm ERR! Actual:   5dd59c4b5a66a54fa2f98aa2433ccc073d2c5452
npm ERR! From:     https://registry.npmjs.org/wd/-/wd-0.1.4.tgz
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR!     <https://github.com/npm/npm/issues>

This is a pain in the arse error that I've experienced a few times. The only "fix" is to bump the version and republish to npm. I've raised this bug with them a few times and it's been fixed, broken, refixed and rebroken again. I suspect you initially published this during one of it's downtimes

XSS test false negative

When using ent I escaped more characters then the ones you do in tests for the XSS lesson. As a result it fails when it should pass.

Maybe test for bad chars?

Lots of C++ compile errors during install

CXX(target) Release/obj.target/leveldown/src/batch.o
In file included from ../src/batch.cc:5:0:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h: In function ‘void NanThrowError(const char*)’:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:209:7: error: ‘ThrowException’ is not a member of ‘v8’
v8::ThrowException(fun(v8::String::New(errmsg)));
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:213:5: note: in expansion of macro ‘_NAN_THROW_ERROR’
_NAN_THROW_ERROR(v8::Exception::Error, errmsg);
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:209:30: error: ‘New’ is not a member of ‘v8::String’
v8::ThrowException(fun(v8::String::New(errmsg)));
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:213:5: note: in expansion of macro ‘_NAN_THROW_ERROR’
_NAN_THROW_ERROR(v8::Exception::Error, errmsg);
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h: In function ‘void NanThrowError(v8::Handlev8::Value)’:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:218:5: error: ‘ThrowException’ is not a member of ‘v8’
v8::ThrowException(error);
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h: In function ‘void NanThrowError(const char*, int)’:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:222:53: error: ‘New’ is not a member of ‘v8::String’
v8::Localv8::Value err = v8::Exception::Error(v8::String::New(msg));
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:224:14: error: ‘New’ is not a member of ‘v8::String’
obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber));
^
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:224:65: error: no matching function for call to ‘v8::Int32::New(const int&)’
obj->Set(v8::String::New("code"), v8::Int32::New(errorNumber));
^
In file included from /home/molsson/.node-gyp/7.7.2/include/node/node.h:42:0,
from ../src/batch.cc:1:
/home/molsson/.node-gyp/7.7.2/include/node/v8.h:2770:25: note: candidate: static v8::Localv8::Integer v8::Integer::New(v8::Isolate*, int32_t)
static Local New(Isolate* isolate, int32_t value);
^
/home/molsson/.node-gyp/7.7.2/include/node/v8.h:2770:25: note: candidate expects 2 arguments, 1 provided
In file included from ../src/batch.cc:5:0:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h: In function ‘void NanThrowTypeError(const char*)’:
/home/molsson/opt/node-v7.7.2-linux-x64/lib/node_modules/security-adventure/node_modules/nan/nan.h:209:7: error: ‘ThrowException’ is not a member of ‘v8’
v8::ThrowException(fun(v8::String::New(errmsg))); \

No Windows support

npm test doesn't even work because our setting of an environment variable in the command-line isn't understood.

Due to toolness/phantom-wd-runner#1, there's a phantomjs process lying around after the tests finish.

The ReDoS verifier also fails with a ENOENT from spawn.

Any help guides?

Hey, trying to work my way through the security adventure. On challenge 1 - Vulnerability: Regular Expression Denial of Service - the link tells you about the problem. I have figured out how to get the app to hang. No idea how to fix it though.

Done some googling but short of a timeout I can't figure out how you are supposed to fix this issue. It would be good to have some tips on the direction expected.

Like learnyounode has some pointers for each exercise. Love the idea of this repo, just as a beginner to app security could do with some guidance on this one.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.