I am writing a program that needs to use Dropbox's webhooks. I haven't been able to find any Go implementation already in place, so I've decided to write mine. Unfortunately, it doesn't seem to work.
I think the issue here is with hmac, as I am most probably doing something wrong, but I just can't seem to understand where exactly the issue here. Any idea?
The following is what I have:
package dboxwebhook
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"errors"
"io"
"io/ioutil"
"log"
)
type Signature struct {
AppSecret []byte
Signature []byte
}
func (w *Signature) Check(reqBody io.ReadCloser) error {
if bytes.Compare(w.Signature, nil) == 0 {
return errors.New("DropBox signature doesnt exist")
}
// building HMAC key (https://golang.org/pkg/crypto/hmac/)
mac := hmac.New(sha256.New, w.AppSecret)
requestBody, err := ioutil.ReadAll(reqBody)
if err != nil {
return err
}
mac.Write(requestBody)
expectedMac := mac.Sum(nil)
log.Println(w.AppSecret)
log.Println(expectedMac)
log.Println(w.Signature)
// compare if it corresponds with the signature sent by DropBox
comparison := hmac.Equal(w.Signature, expectedMac)
if !comparison {
return errors.New("Signature Check unsuccessful")
}
return nil
}
To test this, the only way I know is to use this Python script from Dropbox.
The Dropbox signature is sent as string in the HTTP header X-Dropbox-Signature.
So as to use it with hmac.Equal, you need to decode the hex string representation to a slice of byte first using package encoding/hex.
import "encoding/hex"
[...]
hexSignature, err := hex.DecodeString(w.Signature)
[...]
Then you can use the hex bytes in the comparison
[...]
// compare if it corresponds with the signature sent by DropBox
comparison := hmac.Equal(hexSignature, expectedMac)
[...]
Related
Context: I'm trying to resolve this issue.
In other words, there's a NormalizeJsonString() for JSON strings (see this for more context:
// Takes a value containing JSON string and passes it through
// the JSON parser to normalize it, returns either a parsing
// error or normalized JSON string.
func NormalizeJsonString(jsonString interface{}) (string, error) {
that allows to have the following code:
return structure.NormalizeJsonString(old) == structure.NormalizeJsonString(new)
but it doesn't work for strings that are proto files (all proto files are guaranteed to have just one message definition). For example, I could see:
syntax = "proto3";
- package bar.proto;
+ package bar.proto;
option java_outer_classname = "FooProto";
message Foo {
...
- int64 xyz = 3;
+ int64 xyz = 3;
Is there NormalizeProtoString available in some Go SDKs? I found MessageDifferencer but it's in C++ only. Another option I considered was to replace all new lines / group of whitespaces with a single whitespace but it's a little bit hacky.
To do this in a semantic fashion, the proto definitions should really be parsed. Naively stripping and/or replacing whitespace may get you somewhere, but likely will have gotchas.
As far as I'm aware the latest official Go protobuf package don't have anything to handle parsing protobuf definitions - the protoc compiler handles that side of affairs, and this is written in C++
There would be options to execute the protoc compiler to get hold of the descriptor set output (e.g. protoc --descriptor_set_out=...), however I'm guessing this would also be slightly haphazard considering it requires one to have protoc available - and version differences could potentially cause problems too.
Assuming that is no go, one further option is to use a 3rd party parser written in Go - github.com/yoheimuta/go-protoparser seems to handle things quite well. One slight issue when making comparisons is that the parser records meta information about source line + column positions for each type; however it is relatively easy to make a comparison and ignore these, by using github.com/google/go-cmp
For example:
package main
import (
"fmt"
"log"
"os"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/yoheimuta/go-protoparser/v4"
"github.com/yoheimuta/go-protoparser/v4/parser"
"github.com/yoheimuta/go-protoparser/v4/parser/meta"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
proto1, err := parseFile("example1.proto")
if err != nil {
return err
}
proto2, err := parseFile("example2.proto")
if err != nil {
return err
}
equal := cmp.Equal(proto1, proto2, cmpopts.IgnoreTypes(meta.Meta{}))
fmt.Printf("equal: %t", equal)
return nil
}
func parseFile(path string) (*parser.Proto, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
return protoparser.Parse(f)
}
outputs:
equal: true
for the example you provided.
I have a file with serialized array in PHP.
The content of the file locks like this
a:2:{i:250;s:7:"my_catz";s:7:"abcd.jp";a:2:{s:11:"category_id";i:250;s:13:"category_name";s:7:"my_catz";}}
The array unserialized is this
(
[250] => my_catz
[abcd.jp] => Array
(
[category_id] => 250
[category_name] => my_catz
)
)
Now, i want to get the content of the file in GO, unserialize it convert it to an array.
In GO i can get the content of the file using
dat, err := os.ReadFile("/etc/squid3/compiled-categories.db")
if err != nil {
if e.Debug {
log.Printf("error reading /etc/squid3/compiled-categories.db: ", err)
}
}
And unserialized it using github.com/techoner/gophp library
package categorization
import (
"fmt"
"os"
"github.com/techoner/gophp"
"log"
"errors"
)
type Data struct {
Website string
Debug bool
}
func (e Data) CheckPersonalCategories() (int,string) {
if e.Debug {
log.Printf("Checking Personal Categories")
}
if _, err := os.Stat("/etc/squid3/compiled-categories.db"); errors.Is(err, os.ErrNotExist) {
if e.Debug {
log.Printf("/etc/squid3/compiled-categories.db not exit: ", err)
}
return 0,""
}
dat, err := os.ReadFile("/etc/squid3/compiled-categories.db")
if err != nil {
if e.Debug {
log.Printf("error reading /etc/squid3/compiled-categories.db: ", err)
}
}
out, _ := gophp.Unserialize(dat)
fmt.Println(out["abcd.jp"])
return 0,""
}
But I can't access to the array, for example, when I try access to array key using out["abcd.jp"] i get this error message
invalid operation: out["abcd.jp"] (type interface {} does not support indexing)
The result of out is
map[250:my_catz abcd.jp:map[category_id:250 category_name:my_catz]]
Seams that is unserializing
Don't make assumptions about what is and isn't succeeding in your code. Error responses are the only reliable way to know whether a function succeeded. In this case the assumption may hold, but ignoring errors is always a mistake. Invest time in catching errors and at least panic them - don't instead waste your time ignoring errors and then trying to debug unreliable code.
invalid operation: out["abcd.jp"] (type interface {} does not support indexing)
The package you're using unfortunately doesn't provide any documentation so you have to read the source to understand that gophp.Unserialize returns (interface{}, error). This makes sense; php can serialize any value, so Unserialize must be able to return any value.
out is therefore an interface{} whose underlying value depends on the data. To turn an interface{} into a particular value requires a type assertion. In this case, we think the underlying data should be map[string]interface{}. So we need to do a type assertion:
mout, ok := out.(map[string]interface{})
Before we get to the working code, one more point I'd like you to think about. Look at the code below: I started it from your code, but the resemblance is very slight. I took out almost all the code because it was completely irrelevant to your question. I added the input data to the code to make a minimal reproduction of your code (as I asked you to do and you declined to do). This is a very good use of your time for 2 reasons: first, it makes it a lot easier to get answers (both because it shows sufficient effort on your part and because it simplifies the description of the problem), and second, because it's excellent practice for debugging. I make minimal reproductions of code flows all the time to better understand how to do things.
You'll notice you can run this code now without any additional effort. That's the right way to provide a minimal reproducible example - not with a chunk of mostly irrelevant code which still can't be executed by anybody.
The Go Plaground is a great way to demonstrate go-specific code that others can execute and investigate. You can also see the code below at https://go.dev/play/p/QfCl08Gx53e
package main
import (
"fmt"
"github.com/techoner/gophp"
)
type Data struct {
Website string
Debug bool
}
func main() {
var dat = []byte(`a:2:{i:250;s:7:"my_catz";s:7:"abcd.jp";a:2:{s:11:"category_id";i:250;s:13:"category_name";s:7:"my_catz";}}`)
out, err := gophp.Unserialize(dat)
if err != nil {
panic(err)
}
if mout, ok := out.(map[string]interface{}); ok {
fmt.Println(mout["abcd.jp"])
}
}
Regarding the Uber documentation, I'm trying to verify in order to trust the POST in our API.
https://developer.uber.com/docs/riders/guides/webhooks#security
To do so, I'd need to encrypt with a client_secret in sha256.
Im doing that in Go language.
My issue is, I can't have the same encrypted message than the Uber-Signature.
Here's the code used:
package main
import (
"fmt"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func main() {
secret := "mySecretCode"
msg := `{"event_id":"af4189d4-6a4c-48b5-ab83-8a59d6b03284","resource_href":"https://sandbox-api.uber.com/v1/requests/f09bdc07-509b-4d91-859c-8883ac7fc04a","meta":{"status":"processing","rider_id":"8LSgK24vbx3bcLWThqQyMI_1T_ErGeb1SIHAO3wHX4ycPNvKGf_WsNf14PgVW8el7cBA_lemjMxKkcngZk945K_fCMXQARIfNZ8QOv7VWoQnwpSmUSKUgnYE1WEk-aHyrA==","user_id":"eb0e5df4-3d65-40a0-aff7-40bf06e15727","resource_id":"f09bdc07-509b-4d91-859c-8883ac7fc04a"},"event_type":"requests.status_changed","event_time":1554990665}`
fmt.Printf("Secret: %s Data: %s\n", secret, msg)
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(msg))
encodedMsg := hex.EncodeToString(h.Sum(nil))
fmt.Println(encodedMsg)
}
I'm expecting this:
3795f9ad1c5fe0ae4cd0d10d7a60ccb9f4409c3ae23eb0d0e448fb509f994faf
But I got this:
350b4ed376e3e4a490dfa7ed4701da7be46e9454d5893bc63f5040bffde19fa8
Here's the code: https://play.golang.org/p/PHPFIDxXA8T
Any idea why?
I am a beginner in Golang.
I have a problem with variable type assigning from user input.
When the user enters data like "2012BV352" I need to be able to ignore the BV and pass 2012352 to my next function.
There has a package name gopkg.in/validator.v2 in doc
But what it returns is whether or not the variable is safe or not.
I need to cut off the unusual things.
Any idea on how to achieve this?
You could write your own sanitizing methods and if it becomes something you'll be using more often, I'd package it out and add other methods to cover more use cases.
I provide two different ways to achieve the same result. One is commented out.
I haven't run any benchmarks so i couldn't tell you for certain which is more performant, but you could write your own tests if you wanted to figure it out. It would also expose another important aspect of Go and in my opinion one of it's more powerful tools... testing.
package main
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
)
// using a regex here which simply targets all digits and ignores everything else. I make it a global var and use MustCompile because the
// regex doesn't need to be created every time.
var extractInts = regexp.MustCompile(`\d+`)
func SanitizeStringToInt(input string) (int, error) {
m := extractInts.FindAllString(input, -1)
s := strings.Join(m, "")
return strconv.Atoi(s)
}
/*
// if you didn't want to use regex you could use a for loop
func SanitizeStringToInt(input string) (int, error) {
var s string
for _, r := range input {
if !unicode.IsLetter(r) {
s += string(r)
}
}
return strconv.Atoi(s)
}
*/
func main() {
a := "2012BV352"
n, err := SanitizeStringToInt(a)
if err != nil {
log.Fatal(err)
}
fmt.Println(n)
}
I'm coding in golang some tools to make my life easier and I'm not understanding at all how the types in the net package works. This is part of my code:
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
type configFile struct {
gateway, net net.IP
mask, port int
telnetUser, telnetPasswd string
}
var dataConfig configFile
func createConfigFile() {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Let's fill te config file for the application.")
fmt.Println("Which is your gateway IP?")
readGateway, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
dataConfig.gateway = net.ParseIP(readGateway)
if dataConfig.gateway == nil {
log.Fatal("Problem here")
} else {
fmt.Println("Your gateway is: ", dataConfig.gateway.String())
}
}
My problem is the next:
I want to read an IP address from the command line and storage it in the configFile object, which I will user later to create a .json file with all the configuration of my program.
When I read from the command line the IP address the readGateway variable storages it ok, that's expected, but when I try to make
dataConfig.gateway = net.ParseIP(readGateway)
and I try to cast the string object to a net.IP object I'm always getting a nill in the dataConfig.gateway field, so I'm not able to work with that parameter neither convert it to a string.
Could somebody help me?
Thanks in advance.
bufio.Reader.ReadString's docs explain (emphasis mine)
ReadString reads until the first occurrence of delim in the input,
returning a string containing the data up to and including the
delimiter
So readGateway ends up looking like "192.168.1.1\n". Your newline delimiter would not exist in a properly formatted IP address, which means when you parse it with net.ParseIP it's kicking it out as nil.
You can use strings.TrimRight to trim out the newline:
readGateway, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
readGateway = strings.TrimRight(readGateway, "\n")
The documentation of net.ParseIP(s string) states that if the string s provided is not a valid textual representation of an IP address the function will return a nil value, so that must be the case.
Please log the string s before calling net.ParseIP so you can check if you are passing and reading it properly in the program.