Giter VIP home page Giter VIP logo

csrf-login's Introduction

csrf-login

Login from command line to the websites that use CSRF protection

NPM

Build status dependencies devdependencies semantic-release manpm

Why

CSRF tokens are a good security practice. A login form page contains a hidden input field that is sent together with the username / password pair. The server checks if the sent data contains the valid CSRF field before trying to authenticate the user.

<form method="POST" action="/login/" id="loginform">
    <input type="hidden" name="csrfmiddlewaretoken" value="<long random hash>"" />
    <input type="email" name="email" />
    <input type="password" name="password" />
</form>

There are two components to the protection: the hidden input value and a cookie. In order to successfully execute requests, for example the login POST, one needs to fetch the login page, grab the middleware token and use it to form the valid POST request, plus use the same value as a cookie when making the POST request!

csrf-login allows you to login from command line to websites that use CSRF token protection.

Install and use

Install and use under Node

npm install csrf-login --save

Create a new file csrf.json in the current working folder, place the following custom parameters into this configuration file. For example,

{
  "host": "http://my-dev-server:3000",
  "loginFormId": "loginform",
  "tokenFieldName": "csrfmiddlewaretoken",
  "loginPath": "/accounts/login/"
}

You can take a look at the defaults in src/defaults.json

From your code use the function

var csrfLogin = require('csrf-login');
csrfLogin({
    username: username,
    password: password
}).then(function (info) {
  // info = { request, requestAsync, response, conf };
  return info.requestAsync('/api/foo', { some: params });
}).then(function (data) { });

To get username and password from the user, you can use get-username-and-password.

Passing options

Instead of a JSON file, you can pass options as an object

var csrfLogin = require('csrf-login');
csrfLogin({
  host: 'https://server.com',
  username: username,
  password: password
})

You can also pass folder with the config file, for example if the csrf.json file is in the same as the caller source file. The options from the loaded file will combined with additional options.

var csrfLogin = require('csrf-login');
csrfLogin({
  folder: __dirname,
  password: password
})

You can also pass options via environment variables

USERNAME=me PASSWORD=test123 npm test

Returned object

The returned object info has several properties

csrfLogin(user)
  .then(function (info) {
    console.log(Object.keys(info))
    // [ 'request', 'requestAsync', 'response', 'config', 'jar' ]
  })

info.response

This is the response object from the login call

info.request

The request function you can use to execute other API calls (the session cookie is set for example)

csrfLogin()
  .then(function (info) {
    info.request('/some/other/end/point', function (error, response, body) {
      //Act on the response...
    });
  });

If you want to use promises instead of callbacks, use the requestAsync property

info.requestAsync

The same request object, but wrapped in a promise-returning function. For example to make another API JSON request

csrfLogin()
  .then(function (info) {
    return info.requestAsync({
      url: '/some/data/api',
      json: true
    });
  })
  .then(function (data) {
    console.log('got data from /some/data/api');
    console.log(data);
  })
  .catch(onError)
  .done();

info.config

The full configuration object, created from csrf.json, environment and command line arguments. Uses nconf. One can get the host for example

csrfLogin()
  .then(function (info) {
    console.log('logged into', info.get('host'));
  });

info.jar

The cookie jar after the login. Will contain session / auth cookies for requests.

Debugging

If you need to debug the login process, run the module with debug logging option

DEBUG=csrf node client.js

Under the hood, csrf-login is using request module, and you can enable the request debug logging

DEBUG=csrf NODE_DEBUG=request node client.js

There is a scrip demo/demo.js you can use to quickly test the login feature.

Optional Configuration

By default, a form is looked up by Id and the submitted login form fields are expected to be named email and password. You can override these defaults in the config using the loginFormSelector, loginUsernameField, and loginPasswordField.

{
  "host": "http://my-dev-server:3000",
  "loginFormSelector": "class='myForm'",
  "loginUsernameField": "username",
  "loginPasswordField": "userPassword",
  "tokenFieldName": "csrfmiddlewaretoken",
  "loginPath": "/accounts/login/"
}

Demo

I have coded a small demo server, you can start it manually using npm run demo-server then open localhost:3000, localhost:3000/login pages. You can also run unit tests against the demo server and see how the token is captured and used in the unit tests using npm run demo-test command.

Small print

Author: Gleb Bahmutov © 2015

License: MIT - do anything with the code, but don't blame me if it does not work.

Spread the word: tweet, star on github, etc.

Support: if you find any problems with this module, email / tweet / open issue on Github

MIT License

Copyright (c) 2015 Gleb Bahmutov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

csrf-login's People

Contributors

bahmutov avatar billyjanitsch avatar jeffreypriebe avatar

Stargazers

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

Watchers

 avatar

csrf-login's Issues

Can't login to ASPX CSRF secured websites

Hey bahmutov,

I just experimented in accessing a CSRF protected ASPX Login form - unfortunately I was not able to do any successfull login.

The ASPX Login seems to have more than one hidden field so in the first steps I simply enhanced your code to gather up to three tokens from the page and add it to the form during the post.

I checked and saw they were successfully added with wireshark by inspecting the POST.
Anyhow I always get referred back to the login page.

As my understanding of ASPX is pretty almost nothing more than what I have already found out and written here I thought maybe you have an idea and perhaps this could be a nice feature for csrf-login.

Login problems

Hi

WHen running the code below with:
DEBUG=csrf node csrfloginshort.js
I get hints of success:
csrf trying to login 72352249 +5ms csrf success login to undefined +52ms csrf jar RequestJar { _jar: CookieJar { enableLooseMode: true, store: { idx: { 'blablaserver.internal': { '/': { clientsession: Cookie="clientsession=uirs7e3lv6jrug24s8g37tjvt2; Path=/; hostOnly=true; aAge=50ms; cAge=113ms" } }, null: { '/': { csrftoken: Cookie="csrftoken=a9745bba2dd251258eaf02634d954754f2b278a47a1b1cd151417f1d7f44fa39; Path=/; hostOnly=true; aAge=52ms; cAge=52ms" } } } } } } +0ms

But the html in response.body is a new login page (the site redirects to a login page if you are not logged in), showing the login did not work.

The site also redirect to another page if the login was successful.
So I really want to chain getting
result.request(/customer/interestingpage', function (error, response, body)
after logging in.

Code for csrfloginshort.js

var csrfLogin = require('csrf-login'); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var options = { loginFormId: 'login-form', tokenFieldName: 'csrfToken', loginPath: '/customer/login', loginUsernameField : "user_name", loginPasswordField : "user_password", username: "myuser", password: "123456", host: "https://blablaserver.internal:8469" };

console.log('trying to login', options.username, 'to', options.host); csrfLogin(options) .then(function (result) { result.request('/customer/login', function (error, response, body) { console.log(response.body); console.log(result.jar._jar.store); }) })

Any suggestion would be appreciated

Error on Linkedin

Hello,
I did not have time to test it on other websites, I gave you @bahmutov access to my private repo.
I will publish publicly my work later on with more features and prolly also an interface made with Vue 2.

I created my own User Interface so that I can replace the .env config file with the data coming from an external API or from a DB connection.

However, I tested the code also with the config file with the default name.

Thank you for your help.

All the best,
Davide

The error I have using this on Linkedin is:
node:26190) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Could not find login form

This is the important portion of the code in regard to csrf-login.

----user.service.ts-------------

/**
 * Created by Davide Pugliese on 31/03/17.
 */
import {Observable, Subscribable} from "rxjs/Observable";
import "rxjs/add/observable/from";
import "rxjs/add/observable/fromPromise";
import "rxjs/add/observable/of";
import "rxjs/add/operator/map";
import "rxjs/add/observable/defer";


import * as path from 'path';
import {Observer, Subject} from "rxjs";
// const request = require('request');
const fs = require('fs');
const Promise = require('promise');

interface IUser {
    host: String;
    username: String;
    password: String;
    loginFormSelector: String;
    loginUsernameField: String;
    loginPasswordField: String;
    tokenFieldName: String;
    loginPath: String;
}

class User <Observable> implements IUser {
    public host;
    public username;
    public password;
    public loginFormSelector;
    public loginUsernameField;
    public loginPasswordField;
    public tokenFieldName;
    public loginPath;
}

class UserFactory {

    build()  {

        //We create a promise inside a factory class in order to decouple this precise "user feed" from the rest of the code.
        //In other words the code is ready for multiple "user feeds" as for example an API without having to refactor
        //later on.

        let readFile = Promise.denodeify(require('fs').readFile);

        return readFile(path.join(__dirname, '..', '../.env.json'), 'utf8')
            .then(
                (x) => {
                    let a = JSON.parse(x);

                    let user = new User();
                    user.host = a.host;
                    user.username = a.username;
                    user.password = a.password;
                    user.loginFormSelector = a.loginFormSelector;
                    user.loginUsernameField = a.loginUsernameField;
                    user.loginPasswordField = a.loginPasswordField;
                    user.tokenFieldName = a.tokenFieldName;
                    user.loginPath = a.loginPath;
                    console.log(JSON.stringify(user));
                    return user;
                }
            );
    }
}


let user  = new UserFactory();

// // var user = Observable.of(new User()).map(o => JSON.stringify(o));
export let userObservable: Observable<any> = Observable.from(user.build());
// userObservable.subscribe(x => console.log(JSON.stringify(x)));

--------linkedin.data.service.ts---------------------

/**
 * Created by Davide Pugliese on 31/03/17.
 */
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/from";
import "rxjs/add/observable/fromPromise";
import "rxjs/add/operator/do";
import "rxjs/add/operator/map";
let request = require('request');
import { userObservable } from "./user.service";

import * as path from 'path';
let FileCookieStore = require('tough-cookie-filestore');
let csrfLogin = require('csrf-login');
// const fs = require('fs');
// const Promise = require("bluebird");
// let fs = Promise.promisifyAll(require("fs"));
const Promise = require('promise');
let readFile = Promise.denodeify(require('fs').readFile);
const efs = require('extfs');


class LinkedinDataService {


    constructor() {
    }


    getCookie = function () {

        efs.isEmpty(path.join(__dirname, '..', '../dist/cookies/cookies.json'), function (empty) {
            //this is executed only if the file is empty, this avoids having the same file written multiple times
            //hence a file that contains invalid JSON.
            if (empty) {
                let j = request.jar(new FileCookieStore(path.join(__dirname, '..', '../dist/cookies/cookies.json')));
                request = request.defaults({ jar : j });
                console.log(empty);
                return request('https://www.linkedin.com', function() {
                    request('https://www.linkedin.com/uas/login-submit');

                });
           }

        });

    };

    createCookieObject (callback): Promise<any> {
        callback();

        return readFile(path.join(__dirname, '..', '../dist/cookies/cookies.json'), 'utf8')
            .then(
                (x) => {
                    let a = JSON.parse(x);
                    return a;
                }
            );
    }


    getData<T>(firstname:string, lastname: string)  {



        let createCookie: Promise<any> = this.createCookieObject(() => {
             this.getCookie();
            }
        );

       // let cookieObservable: Observable<any> = Observable.fromPromise(createCookie);
       //
       // return cookieObservable.subscribe(
       //    x => console.log(JSON.stringify(x))
       // );


        return userObservable.subscribe(
            (user) => {
                csrfLogin(user)
                    .then(function (info) {
                        console.log(Object.keys(info));
                        // [ 'request', 'requestAsync', 'response', 'config', 'jar' ]
                    })
            }
        );
    }


}
export  { LinkedinDataService };

--------.env.json------

{
  "host": "https://www.linkedin.com",
  "username": "Linkedin Username",
  "password": "Linkedin Password",
  "loginFormSelector": "class='login-form'",
  "loginUsernameField": "session_key",
  "loginPasswordField": "session_password",
  "tokenFieldName": "loginCsrfParam",
  "loginPath": "/uas/login-submit/"

}

Send referer header

HTTPS serves usually need this header for security. Make sure to send one

Jar reuse

Hello. I have some question. How can i use again data from info.jar on next use crsfLogin() ? I have to scrapping site more than one times and after ~30 requests that will give Captcha. So i think do not login on every crsfLogin()

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.