Skip to content

Commit a5b48f5

Browse files
committed
Add a toggle to disable signature verification
1 parent 7c9a81e commit a5b48f5

File tree

7 files changed

+65
-10
lines changed

7 files changed

+65
-10
lines changed

botogram/bot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ def __init__(self, api_connection):
5555

5656
self.process_backlog = False
5757

58+
self.validate_callback_signatures = True
59+
5860
self._lang = ""
5961
self._lang_inst = None
6062

@@ -119,6 +121,13 @@ def __reduce__(self):
119121
return object.__reduce__(self)
120122

121123
def __setattr__(self, name, value):
124+
# Warn about disabled callback validation
125+
if name == "validate_callback_signatures" and not value:
126+
self.logger.warn("Your code disabled signature validation for "
127+
"callbacks!")
128+
self.logger.warn("This can cause security issues. Please enable "
129+
"it again.")
130+
122131
# Use the standard __setattr__
123132
return object.__setattr__(self, name, value)
124133

@@ -256,6 +265,7 @@ def freeze(self):
256265
return frozenbot.FrozenBot(self.api, self.about, self.owner,
257266
self._hide_commands, self.before_help,
258267
self.after_help, self.link_preview_in_help,
268+
self.validate_callback_signatures,
259269
self.process_backlog, self.lang,
260270
self.itself, self._commands_re,
261271
self._commands, chains, self._scheduler,

botogram/callbacks.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ def parse_callback_data(bot, chat, raw):
116116
name = prelude[16:]
117117
data = raw[32:]
118118

119-
correct = get_signature(bot, chat, name, data)
120-
if not crypto.compare(correct, signature):
121-
raise crypto.TamperedMessageError
119+
# Don't check the signature if the user explicitly disabled the check
120+
if bot.validate_callback_signatures:
121+
correct = get_signature(bot, chat, name, data)
122+
if not crypto.compare(correct, signature):
123+
raise crypto.TamperedMessageError
122124

123125
if data:
124126
return name, data.decode("utf-8")

botogram/frozenbot.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ class FrozenBot:
3333
"""A frozen version of botogram.Bot"""
3434

3535
def __init__(self, api, about, owner, hide_commands, before_help,
36-
after_help, link_preview_in_help, process_backlog, lang,
37-
itself, commands_re, commands, chains, scheduler,
38-
main_component_id, bot_id, shared_memory, update_processors):
36+
after_help, link_preview_in_help,
37+
validate_callback_signatures, process_backlog, lang, itself,
38+
commands_re, commands, chains, scheduler, main_component_id,
39+
bot_id, shared_memory, update_processors):
3940
# This attribute should be added with the default setattr, because is
4041
# needed by the custom setattr
4142
object.__setattr__(self, "_frozen", False)
@@ -48,6 +49,7 @@ def __init__(self, api, about, owner, hide_commands, before_help,
4849
self.before_help = before_help
4950
self.after_help = after_help
5051
self.link_preview_in_help = link_preview_in_help
52+
self.validate_callback_signatures = validate_callback_signatures
5153
self.process_backlog = process_backlog
5254
self.lang = lang
5355
self._commands_re = commands_re
@@ -77,10 +79,10 @@ def __reduce__(self):
7779
args = (
7880
self.api, self.about, self.owner, self._hide_commands,
7981
self.before_help, self.after_help, self.link_preview_in_help,
80-
self.process_backlog, self.lang, self.itself, self._commands_re,
81-
self._commands, self._chains, self._scheduler,
82-
self._main_component_id, self._bot_id, self._shared_memory,
83-
self._update_processors,
82+
self.validate_callback_signatures, self.process_backlog, self.lang,
83+
self.itself, self._commands_re, self._commands, self._chains,
84+
self._scheduler, self._main_component_id, self._bot_id,
85+
self._shared_memory, self._update_processors,
8486
)
8587
return restore, args
8688

docs/api/bot.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ components.
6060

6161
.. versionadded:: 0.4
6262

63+
.. py:attribute:: validate_callback_signatures
64+
65+
Enable or disable signature verification for callbacks. Disabling them
66+
can cause security issues for your bot, and it's advised to do so only if
67+
you changed the bot token recently. See the :ref:`security section for
68+
callbacks <buttons-security>` for more details.
69+
70+
The default value is **True**.
71+
72+
.. versionadded:: 0.4
73+
6374
.. py:attribute:: process_backlog
6475
6576
A boolean representing if the backlog should be processed. Backlog is

docs/buttons.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,16 @@ This protection is completly transparent: you don't have to do anything to
235235
enable or manage it. The signature is based on the token of the bot though:
236236
this means if you revoke or change the token, all previous callbacks will
237237
become invalid, which may annoy your users because old buttons stop working.
238+
239+
If you want to avoid disruption after changing your bot's token, it's advised
240+
to disable signature verification for a few days: it lowers the security of
241+
your bot, but allows the user to keep using buttons under old messages. In
242+
order to disable the verification you need to add this snippet of code before
243+
the bot is started:
244+
245+
.. code-block:: python
246+
247+
bot.validate_callback_signatures = False
248+
249+
You should remove the snippet after a few days. The bot will print a
250+
warning at startup to remember you to do so.

docs/changelog/0.4.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ New features
2121

2222
* Added support for :ref:`buttons and callbacks <buttons>`
2323

24+
* New attribute :py:attr:`botogram.Bot.validate_callback_signatures`
2425
* New class :py:class:`botogram.Buttons`
2526
* New class :py:class:`botogram.ButtonsRow`
2627
* New class :py:class:`botogram.CallbackQuery`

tests/test_callbacks.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020

2121
import json
2222

23+
import pytest
24+
2325
from botogram.callbacks import Buttons, parse_callback_data, get_callback_data
2426
from botogram.callbacks import hashed_callback_name
2527
from botogram.components import Component
2628
from botogram.context import Context
29+
from botogram.crypto import TamperedMessageError
2730
from botogram.hooks import Hook
2831

2932

@@ -84,3 +87,16 @@ def test_parse_callback_data(bot, sample_update):
8487
hashed_callback_name("test_callback"),
8588
None,
8689
)
90+
91+
with pytest.raises(TamperedMessageError):
92+
raw = get_callback_data(bot, c, "test_callback", "data") + "!"
93+
parse_callback_data(bot, c, raw)
94+
95+
# Now test with disabled signature verification
96+
bot.validate_callback_signatures = False
97+
98+
raw = get_callback_data(bot, c, "test_callback", "data") + "!"
99+
assert parse_callback_data(bot, c, raw) == (
100+
hashed_callback_name("test_callback"),
101+
"data!"
102+
)

0 commit comments

Comments
 (0)