Node.js sql 注入解析错误

Node.js sql injection parse error

我快完成注册了,但我发现了一些漏洞。

每当我添加:?Smith 到我的注册表中时,我都会收到错误消息:

Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '??Smith' at line 1

易受攻击的行(第86行):

connection.query('SELECT * FROM penguins WHERE username = ?' + username, function(err, rows) { + connection.escape(email); + connection.escape(username); + connection.escape(req.body.email); + connection.escape(req.body.nickname);

我试图用 connection.escape(); 来逃避它;但它没有用。电子邮件、用户名和密码都是易受攻击的输入。

我的护照攻略:

const dateTime = new Date().getTime();
const timestamp = Math.floor(dateTime / 1000);
var mysql = require('mysql');
var LocalStrategy = require('passport-local').Strategy;

var connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root'
});

connection.config.queryFormat = function (query, values) {
  if (!values) return query;
  return query.replace(/\:%'"=-?(\w+)/g, function (txt, key) {
    if (values.hasOwnProperty(key)) {
      return this.escape(values[key]);
    }
    return txt;
  }.bind(this));
};



connection.query('USE kitsune');

// expose this function to our app using module.exports
module.exports = function(passport) {

    // =========================================================================
    // passport session setup ==================================================
    // =========================================================================
    // required for persistent login sessions
    // passport needs ability to serialize and unserialize users out of session

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
        connection.query('SELECT * FROM penguins WHERE id = ?' + id, function(err, rows) { + connection.escape(id);
            connection.escape(id);
            id.toString();
            done(err, rows[0]);
        });
    });


    // =========================================================================
    // LOCAL SIGNUP ============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

       passport.use('local-signup', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField: 'username',
        passwordField: 'password',
        gameusernameField: 'username',
        nicknameField: 'nickname',
        passReqToCallback: true // allows us to pass back the entire request to the callback
    },

    function(req, username, password, done) {

        // here you read from req
        const email = req.body.email
        const nickname = req.body.nickname
        const inventory = '%1'; // This is what the user gets on register. You can set this to anything that you want like: %1%2%3%4%5%6%7%8%9%10%11%12%13%14%15%16
        const moderator = '0';
        //const registrationdate = timestamp
        const igloo = '1';
        const igloos = '1';
        console.log("Inventory is set to: " + inventory);
        console.log("Moderator is set to: " + moderator);
        console.log("Igloo is set to: " + igloo);
        console.log("Igloos is set to: " + igloos);

    passport.serializeUser(function(username, done) {
        done(null, username);
    });

        // find a user whose email is the same as the forms email
        // we are checking to see if the user trying to login already exists
        connection.query('SELECT * FROM penguins WHERE username = ?' + username, function(err, rows) { + connection.escape(email); + connection.escape(username); + connection.escape(req.body.email); + connection.escape(req.body.nickname);
            username.toString();
            password.toString();
            email.toString();
            console.log(rows);
            console.log("above row object");
            if (err) return done(err);
            if (rows.length) {
                return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
            } else {

                // if there is no user with that email
                // create the user
                var newUserMysql = new Object();
                newUserMysql.igloos = igloos;
                newUserMysql.igloo = igloo;
                //newUserMysql.registrationdate = registrationdate;
                newUserMysql.moderator = moderator;
                newUserMysql.inventory = inventory;
                newUserMysql.email = email;
                newUserMysql.password = password; // use the generateHash function in our user model
                newUserMysql.username = username;
                newUserMysql.nickname = nickname;
                var insertQuery = "INSERT INTO penguins (igloos, igloo, moderator, inventory, email, password, username, nickname ) VALUES ('1','1','" + moderator + "','" + inventory + "','" + email +  "', + UPPER(MD5('" + password + "')), '" + username + "', '" + username + "')"; + connection.escape(username); + connection.escape(password); + connection.escape(nickname); + connection.escape(email);
                connection.escape(nickname);
                connection.escape(inventory);
                connection.escape(igloo);
                connection.escape(igloos);
                connection.escape(moderator);
                connection.escape(id);
                connection.escape(username);
                connection.escape(email);
                connection.escape(nickname);
                connection.escape(password);
                username.toString();
                password.toString();
                igloos.toString();
                igloo.toString();
                moderator.toString();
                inventory.toString();
                email.toString();
                nickname.toString();
                id.toString();
                console.log(insertQuery);
                connection.query(insertQuery, function(err, rows) {
                    newUserMysql.id = rows.insertId;
                    return done(null, newUserMysql);
                    });

            }
        });

    }));


    // =========================================================================
    // LOCAL LOGIN =============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-login', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true // allows us to pass back the entire request to the callback
    },

    function(req, email, password, username, nickname, done) { // callback with email and password from our form
        connection.query('SELECT * FROM penguins WHERE username = ?' + username, function(err, rows) {
            username.toString();
            connection.escape(username);
            if (err) return done(err);
            if (!rows.length) {
                return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
            }

            // if the user is found but the password is wrong
            if (!(rows[0].password == password)) return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            return done(null, rows[0]);

        });



    }));

}

您将字符串连接与占位符混合在一起。简而言之:

从不使用这样的东西,但它在技术上是有效的,即使它很容易受到攻击:

connection.query('SELECT * FROM penguins WHERE username = ' + username, ...

总是使用像这样有效且不易受攻击的东西:

connection.query('SELECT * FROM penguins WHERE username = ?', username, ...

像这样混合使用会产生语法错误:

connection.query('SELECT * FROM penguins WHERE username = ?' + username, ...

因为 'SELECT * FROM penguins WHERE username = ?' 字符串将与 username 的值连接起来,并且可能会产生如下内容:

'SELECT * FROM penguins WHERE username = ?johndoe'

无效 SQL。转义字符串在这里无济于事。

有关字符转义和占位符的更多详细信息,请参阅此答案:

  • How to escape mysql special characters with sockets.io/node.js/javascript

我发现了问题。

易受攻击的行:connection.query('SELECT * FROM penguins WHERE username = ?', username, ...

最后一部分:username = ?

如果有人注册了?在其中,它创建了 SQL 错误。

一个简单的修复:connection.query("select * from penguins where id = "+id,function(err,rows){

另一个修复:connection.query("select * from penguins where username = '"+username+"'",function(err,rows){

干杯!