【问题标题】:Node.js sql injection parse errorNode.js sql注入解析错误
【发布时间】:2017-12-22 00:58:46
【问题描述】:

我几乎完成了我的注册,但我发现了一些漏洞。

每当我在注册表中添加:?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]);

        });



    }));

}

【问题讨论】:

    标签: javascript mysql node.js passport.js passport-local


    【解决方案1】:

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

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

    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。转义字符串在这里无济于事。

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

    【讨论】:

    • 我这样做了:connection.query('SELECT * FROM penguins WHERE username = ?', username, function(err, rows) { 但我仍然收到 sql 错误。
    • 应该是[username]
    • @BenFortune 之后,我收到以下错误: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 '?' at line 1
    • 你做了connection.query('SELECT * FROM penguins WHERE username = ?', [username], ... ?
    • 是的,我做到了:connection.query('SELECT * FROM penguins WHERE username = ?', [username], function(err, rows) {
    【解决方案2】:

    我发现了问题。

    漏洞线路: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){

    干杯!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-18
      • 2017-09-03
      • 1970-01-01
      相关资源
      最近更新 更多