Защити пользователя своего

Nikita Grishko, JUNO

Почему это плохо?

  • Потенциально лишняя нагрузка на сервер
  • Вашу базу пользователей могут утащить

Чего мы хотим?

  • Усложнить жизнь тем кто нас атакует
  • Не делать больно пользователям

OAuth!

Проблемы

  • Если пользователь теряет доступ к стороннему сервису, то теряет и к нашему
  • Если взломан аккаунт от стороннего сервиса, то можно считать, что взломан и от нашего

State

«Произвольная строка, которая будет возвращена вместе с результатом авторизации.»

state = get_random_string()
session.set(state, expire=5 * 60)

redirect_url = 'https://oauth.vk.com/authorize?...&state=' + state
...

State

state = query['state']
if state not in session:
   return Response(status=400)

...

Немного про AccessToken

$ http https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=1/fFBGRNJru1FQd44AzqT3Zg
...
{
  "audience":"8819981768.apps.googleusercontent.com",
  "user_id":"123456789",
  "scope":"profile email",
  "expires_in":436
}

Запасной план

Все должно быть отключаемым

Throttling

  • N (неудачных) запросов в M времени
  • N (неудачных) запросов в M времени для одного IP

Throttling

key = '{user_ip}:{sec}'.format(user_ip=user_ip, sec=int(time.time()))
cache.inrc(key, expire=10 * 60)
end = int(time.time()) + 1
start = end - 60  # one minute
keys = [
    '{user_ip}:{sec}'.format(user_ip=user_ip, sec=sec)
    for sec in range(start, end)
]
attempts = sum(cache.get_many(keys))
if attempts > limit:
    ...

...

Что получает пользовать?

А что с плохими ребятами?

CAPTCHA...

CAPTCHA...

Ненавидишь своего пользователя?

Добавь капчу, пусть он страдает!

CAPTCHA...

  • Сложная — пользователь страдает
  • Простая — легко распознается

Tesseract

ImageMagic

Human-based CAPTCHA solving services

  • DeathByCaptcha
  • Bypass CAPTCHA
  • Image Typerz
  • ...

Proof-of-work

принцип защиты систем от злоупотребления услугами, основанный на необходимости выполнения запрашивающей стороной некоторой достаточно сложной длительной работы, результат которой легко и быстро проверяется обслуживающей стороной

Hashcash

Hashcash

stamp = '{ver}:{bits}:{date}:{resource}:{ext}:{rand}:{counter}'.format(**{
    'ver': 1,
    'bits': 3,
    'date': 1457459496,
    'resource': 'poffw',
    'ext': '',
    'rand': 'TI5gjgHjyGYZtbla',
    'counter': 12481,
})
prefix = '0' * 3

sha3.sha3_512(stamp).hexdigest().startswith(prefix)

poffw

Server

token = random_string()
task = {
    'ver': 1,
    'bits': 3,
    'date': int(time.time()),
    'resource': 'poffw',
    'ext': '',
    'rand': random_string(),
}

await redis.set(token, json.dumps(task))
await redis.expire(token, 60)

Server

$ http 127.0.0.1:8080/login
HTTP/1.1 200 OK
CONTENT-LENGTH: 101
CONTENT-TYPE: application/json; charset=utf-8
DATE: Tue, 08 Mar 2016 18:11:04 GMT
SERVER: Python/3.5 aiohttp/0.21.2
X-POFFW: atIibp2AA2p7xxcW

{
    "bits": 1,
    "date": 1457460664,
    "ext": "",
    "rand": "tUJWHkU1x4Hgjosw",
    "resource": "poffw",
    "ver": 1
}

Server

token = request.headers['X-POFFW']
counter = data['counter']

task = await redis.get(token)
task['counter'] = counter

stamp = '{ver}:{bits}:{date}:{resource}:{ext}:{rand}:{counter}'.format(**task)
prefix = '0' * task['bits']

sha3.sha3_512(stamp).hexdigest().startswith(prefix)

Server

$ http POST 127.0.0.1:8080/login counter=3 'X-POFFW: atIibp2AA2p7xxcW'
HTTP/1.1 200 OK
CONTENT-LENGTH: 0
CONTENT-TYPE: application/json
DATE: Tue, 08 Mar 2016 18:13:50 GMT
SERVER: Python/3.5 aiohttp/0.21.2

Client

var stamp = [task.ver, task.bits, task.date, task.resource, 
             task.ext, task.rand].join(':'),
    prefix = new Array(task.bits + 1).join('0'),
    counter = 1,

    check = function(counter) {
        var hash = CryptoJS.SHA3([stamp, counter].join(':'));
        return String(hash, {
            outputLength: 512
        }).indexOf(prefix) === 0;
    };

while (true) {
    if (check(counter)) {
        return counter;
    }

    counter += 1;
}

Client

var parallel = new Parallel(task);

parallel.require(pathSHA3).spawn(solveWorker).then(function(counter) {
    console.log('Task resolved:', counter);
});

Demo time

reCAPCTHA 2.0

Captcha.ONE

End?

poffw

Two-factor

Auth

Защити пользователя своего

By Nikita Grishko

Защити пользователя своего

[12-03-2016] MinskJS, Minsk

  • 2,061