findOne 기능에 주의

11060 단어 securityjavascript
안녕하세요, 지난 포스트 이후로 저는 정보 보안 산업으로 옮겨 현대 애플리케이션의 취약점에 대해 많은 연구/조사를 시작했습니다.

이 게시물에서는 NoSQL 주입으로부터 웹 애플리케이션을 보호하는 방법을 알아봅니다.

OWASP Top 10 - 2017에 따르면 지난해 가장 빈번하게 발생한 취약점은 취약한 시스템의 특정 지점에 악성코드를 주입하는 것을 의미하는 A1:2017-Injection으로, 가장 많이 알려진 주입은 URL, 형태를 통한 SQL 주입이다. 피해자의 데이터베이스에 악의적인 쿼리를 보낼 수 있습니다.

요즘에는 사용자를 인증하는 API가 있고 비관계형 데이터베이스를 사용하여 이 정보를 저장하는 시스템을 찾는 것이 일반적입니다. 많이 사용되는 은행은 Mongo 입니다.

아래 예에서는 Mongo와 매우 유사한 구문을 가진 NeDB 은행을 사용했습니다.

제어 장치

exports.login = async (req, reply) => {
    try {
        let { user, pass } = req.body

        let result = await findOne({user, pass})

        return reply.code(200).send(result)
    } catch (e) {
        return reply.code(500).send({ success: false, result: 'user/pass not found' })
    }
}


db.findOne

async function findOne(query) {
    return new Promise((resolve, reject) => {
        db.findOne(query, (err, result) => {
            if (err) return reject(err)

            resolve({ success: true, result })
        })
    })
}




우리가 findOne에 전달한 개체가 유효한 개체였기 때문에 로그인이 이루어졌습니다. 즉, user와 pass 모두 데이터베이스에 실제로 존재하는 값을 가집니다.

이 글 초반에 SQL Injection에 대해 언급했는데, NoSQL Injection에 대해 들어보셨나요? 아니다? 자, 이것이 무엇인지 이해하게 될 것입니다. 다음 기능을 참조하십시오.


db.findOne(query, (err, result) => {
    if (err) return reject(err)

    resolve({ success: true, result })
})


기본적으로 이 함수가 하는 일은 사용자 && 패스에 전달한 값이 있는 레코드가 있는지 데이터베이스를 확인하는 것입니다. 여기서는 논리 연산자 &&(및 )를 사용했습니다.

이것은 우리가 적어도 유효한 사용자를 전달하고 전달 대신 TRUE를 반환하는 다른 유효성 검사를 알리면 함수가 작동한다고 생각하게 만들지 않습니다.

Mongo와 NeDB 모두 데이터베이스의 쿼리에 사용할 수 있는 필터(예: $gt)가 있으며 이는 관계 연산자 ">"와 동일합니다. 암호 대신 이 필터를 사용하여 쿼리를 수행해 보겠습니다.



즉, 우리는 데이터베이스에 "wubba"라는 사용자가 있는 레코드가 있는지, pass 값이 "nothing"보다 큰지 묻는 쿼리를 만들었습니다. 해당 이름을 가진 사용자가 있으면 물론 암호가 더 클 것입니다. "아무것도"보다.

동일한 개체{"$ gt": ""}를 user에 전달하고 전달하면 은행은 가지고 있는 첫 번째 레코드를 반환합니다!

이것은 우리가 전달하는 값에 대한 처리를 만들지 않으면 findOne 함수가 위험하다는 것을 보여줍니다. 이 경우 정보가 객체가 아닌지 확인할 수 있습니다.

이를 수정하기 위해 다음 기능을 사용할 수 있습니다.

제어 장치

exports.loginProtected = async (req, reply) => {
    try {
        let { user, pass } = req.body
        await isObject({ user, pass })

        let result = await findOne({user, pass})

        return reply.code(200).send(result)
    } catch (e) {
        return reply.code(500).send({ success: false, result: 'user/pass not found' })
    }
}


객체


async function isObject(params) {
    return new Promise((resolve, reject) => {
        Object.keys(params).forEach((v, i) => {
            if (typeof params[v] === 'object') return reject(false)
        })
        resolve(true)
    })
}


이 사례는 NeDB 은행을 사용하여 재현되었지만 MongoSails/Waterline을 사용하여 시뮬레이션되었습니다. 다른 은행에서 찾으면 여기에 댓글을 달아 다른 사람을 돕습니다 😉

Github 프로젝트 https://github.com/nulldreams/nosql-pentest

좋은 웹페이지 즐겨찾기