Защити пользователя своего
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,208