Protecting callbacks with callback guards.

This commit is contained in:
Ian Byrd 2017-11-26 05:10:06 +02:00
parent eae6345b88
commit 6f068bdacb
No known key found for this signature in database
GPG Key ID: 598F598CA3B8055F
4 changed files with 80 additions and 17 deletions

28
bot.go
View File

@ -20,10 +20,16 @@ func NewBot(pref Settings) (*Bot, error) {
pref.Updates = 100
}
if pref.Guard != nil && pref.Secret == "" {
panic("telebot: can't set guard with no secret")
}
bot := &Bot{
Token: pref.Token,
Updates: make(chan Update, pref.Updates),
Secret: pref.Secret,
Poller: pref.Poller,
Guard: pref.Guard,
handlers: make(map[string]interface{}),
}
@ -41,10 +47,12 @@ func NewBot(pref Settings) (*Bot, error) {
type Bot struct {
Me *User
Token string
Secret string
Updates chan Update
Poller Poller
Errors chan error
Guard Guard
Errors chan error
handlers map[string]interface{}
}
@ -59,6 +67,12 @@ type Settings struct {
// Poller is the provider of Updates.
Poller Poller
// Callback guard (de-)encrypts callback data.
Guard Guard
// Bot secret will be used when encrypting callback data.
Secret string
}
// Update object represents an incoming update.
@ -232,6 +246,10 @@ func (b *Bot) Start() {
if upd.Callback.Data != "" {
data := upd.Callback.Data
if b.Guard != nil {
data = b.Guard.Decrypt(data, b.Secret)
}
if data[0] == '\f' {
match := cbackRx.FindAllStringSubmatch(data, -1)
@ -414,7 +432,7 @@ func (b *Bot) SendAlbum(to Recipient, a Album, options ...interface{}) ([]Messag
}
sendOpts := extractOptions(options)
embedSendOptions(params, sendOpts)
b.embedSendOptions(params, sendOpts)
respJSON, err := b.sendFiles("sendMediaGroup", files, params)
if err != nil {
@ -477,7 +495,7 @@ func (b *Bot) Forward(to Recipient, what *Message, options ...interface{}) (*Mes
}
sendOpts := extractOptions(options)
embedSendOptions(params, sendOpts)
b.embedSendOptions(params, sendOpts)
respJSON, err := b.sendCommand("forwardMessage", params)
if err != nil {
@ -509,7 +527,7 @@ func (b *Bot) Edit(originalMsg Editable, text string, options ...interface{}) (*
}
sendOpts := extractOptions(options)
embedSendOptions(params, sendOpts)
b.embedSendOptions(params, sendOpts)
respJSON, err := b.sendCommand("editMessageText", params)
if err != nil {
@ -714,7 +732,7 @@ func (b *Bot) Pin(message Editable, options ...interface{}) error {
}
sendOpts := extractOptions(options)
embedSendOptions(params, sendOpts)
b.embedSendOptions(params, sendOpts)
respJSON, err := b.sendCommand("pinChatMessage", params)
if err != nil {

40
guard.go Normal file
View File

@ -0,0 +1,40 @@
package telebot
import (
"crypto/sha1"
"fmt"
)
// Guard is a callback guard, it performs some sort
// of encryption for callbacks and callback data.
type Guard interface {
Encrypt(text, secret string) string
Decrypt(ciphertext, secret string) string
}
// XorGuard implements simple XOR encryption.
type XorGuard struct{}
func sha1str(data string) string {
h := sha1.New()
h.Write([]byte(data))
bs := h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
func (x *XorGuard) Encrypt(text, secret string) string {
// and hope for the best
key := sha1str(secret)
ctext := make([]byte, len(text))
for i, _ := range text {
ctext[i] = text[i] ^ key[i%len(key)]
}
return string(ctext)
}
func (x *XorGuard) Decrypt(ctext, secret string) string {
return x.Encrypt(ctext, secret)
}

View File

@ -25,7 +25,7 @@ func (p *Photo) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
"caption": p.Caption,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&p.File, "photo", params)
if err != nil {
@ -44,7 +44,7 @@ func (a *Audio) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
"chat_id": to.Recipient(),
"caption": a.Caption,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&a.File, "audio", params)
if err != nil {
@ -63,7 +63,7 @@ func (d *Document) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error
"chat_id": to.Recipient(),
"caption": d.Caption,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&d.File, "document", params)
if err != nil {
@ -81,7 +81,7 @@ func (s *Sticker) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error)
params := map[string]string{
"chat_id": to.Recipient(),
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&s.File, "sticker", params)
if err != nil {
@ -100,7 +100,7 @@ func (v *Video) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
"chat_id": to.Recipient(),
"caption": v.Caption,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&v.File, "video", params)
if err != nil {
@ -118,7 +118,7 @@ func (v *Voice) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
params := map[string]string{
"chat_id": to.Recipient(),
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&v.File, "voice", params)
if err != nil {
@ -136,7 +136,7 @@ func (v *VideoNote) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, erro
params := map[string]string{
"chat_id": to.Recipient(),
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
msg, err := b.sendObject(&v.File, "videoNote", params)
if err != nil {
@ -156,7 +156,7 @@ func (x *Location) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error
"latitude": fmt.Sprintf("%f", x.Lat),
"longitude": fmt.Sprintf("%f", x.Lng),
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendLocation", params)
if err != nil {
@ -176,7 +176,7 @@ func (v *Venue) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
"address": v.Address,
"foursquare_id": v.FoursquareID,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendVenue", params)
if err != nil {

11
util.go
View File

@ -33,7 +33,7 @@ func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, e
"chat_id": to.Recipient(),
"text": text,
}
embedSendOptions(params, opt)
b.embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendMessage", params)
if err != nil {
@ -132,7 +132,7 @@ func extractOptions(how []interface{}) *SendOptions {
return opts
}
func embedSendOptions(params map[string]string, opt *SendOptions) {
func (b *Bot) embedSendOptions(params map[string]string, opt *SendOptions) {
if opt == nil {
return
}
@ -160,7 +160,12 @@ func embedSendOptions(params map[string]string, opt *SendOptions) {
for j, _ := range keys[i] {
key := &keys[i][j]
if key.Unique != "" {
key.Data = "\f" + key.Unique + "|" + key.Data
data := "\f" + key.Unique + "|" + key.Data
if b.Guard != nil {
data = b.Guard.Encrypt(data, b.Secret)
}
key.Data = data
}
}
}