mirror of
https://wiilab.wiimart.org/wiimart/WiiSOAP
synced 2025-09-05 21:11:02 +02:00
Reduce security
Nintendo makes the unfortunate decision of sending the plaintext token to the client in a few requests. Instead of storing a SHA-256 hash of the MD5 we expect from the Wii, we now store both the computed MD5 and plaintext generated token during registration. The author of this commit apologizes.
This commit is contained in:
parent
7558aeed43
commit
8009f1a510
33
database.sql
33
database.sql
@ -25,6 +25,21 @@ Follow and practice proper security practices before handling user data.
|
|||||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `shop_titles`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `shop_titles`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `shop_titles` (
|
||||||
|
`title_id` varchar(16) NOT NULL,
|
||||||
|
`version` int(11) NOT NULL,
|
||||||
|
`description` mediumtext DEFAULT 'yada yada',
|
||||||
|
PRIMARY KEY (`title_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `owned_titles`
|
-- Table structure for table `owned_titles`
|
||||||
--
|
--
|
||||||
@ -43,21 +58,6 @@ CREATE TABLE `owned_titles` (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `shop_titles`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `shop_titles`;
|
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
|
||||||
CREATE TABLE `shop_titles` (
|
|
||||||
`title_id` varchar(16) NOT NULL,
|
|
||||||
`version` int(11) NOT NULL,
|
|
||||||
`description` mediumtext DEFAULT 'yada yada',
|
|
||||||
PRIMARY KEY (`title_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `userbase`
|
-- Table structure for table `userbase`
|
||||||
--
|
--
|
||||||
@ -67,7 +67,8 @@ DROP TABLE IF EXISTS `userbase`;
|
|||||||
/*!40101 SET character_set_client = utf8 */;
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
CREATE TABLE `userbase` (
|
CREATE TABLE `userbase` (
|
||||||
`DeviceId` varchar(10) NOT NULL,
|
`DeviceId` varchar(10) NOT NULL,
|
||||||
`DeviceToken` varchar(64) NOT NULL COMMENT 'This token should be considered a secret, so after generation only the sha256sum of the md5 the Wii sends is inserted.',
|
`DeviceTokenUnhashed` varchar(21) NOT NULL COMMENT 'Sadly, sometimes we must return the device token in plaintext.',
|
||||||
|
`DeviceToken` varchar(32) NOT NULL COMMENT 'The MD5 of the device token is sent for most requests. We store it to avoid wasting compute time.',
|
||||||
`AccountId` varchar(9) NOT NULL,
|
`AccountId` varchar(9) NOT NULL,
|
||||||
`Region` varchar(3) NOT NULL,
|
`Region` varchar(3) NOT NULL,
|
||||||
`Country` varchar(2) NOT NULL,
|
`Country` varchar(2) NOT NULL,
|
||||||
|
13
ias.go
13
ias.go
@ -19,7 +19,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/sha256"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -34,7 +33,7 @@ var registerUser *sql.Stmt
|
|||||||
|
|
||||||
func iasInitialize() {
|
func iasInitialize() {
|
||||||
var err error
|
var err error
|
||||||
registerUser, err = db.Prepare(`INSERT INTO userbase (DeviceId, DeviceToken, AccountId, Region, Country, Language, SerialNo, DeviceCode) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
registerUser, err = db.Prepare(`INSERT INTO userbase (DeviceId, DeviceTokenUnhashed, DeviceToken, AccountId, Region, Country, Language, SerialNo, DeviceCode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ias initialize: error preparing statement: %v\n", err)
|
log.Fatalf("ias initialize: error preparing statement: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -120,19 +119,13 @@ func register(e *Envelope) {
|
|||||||
// Generate a random 9-digit number, padding zeros as necessary.
|
// Generate a random 9-digit number, padding zeros as necessary.
|
||||||
accountId := fmt.Sprintf("%9d", rand.Intn(999999999))
|
accountId := fmt.Sprintf("%9d", rand.Intn(999999999))
|
||||||
|
|
||||||
// This is where it gets hairy.
|
|
||||||
// Generate a device token, 21 characters...
|
// Generate a device token, 21 characters...
|
||||||
deviceToken := RandString(21)
|
deviceToken := RandString(21)
|
||||||
// ...and then its md5, because the Wii sends this...
|
// ...and then its md5, because the Wii sends this for most requests.
|
||||||
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
|
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
|
||||||
// ...and then the sha256 of that md5.
|
|
||||||
// We'll store this in our database, as storing the md5 itself is effectively the token.
|
|
||||||
// It would not be good for security to directly store the token either.
|
|
||||||
// This is the hash of the md5 represented as a string, not individual byte values.
|
|
||||||
doublyHashedDeviceToken := fmt.Sprintf("%x", sha256.Sum256([]byte(md5DeviceToken)))
|
|
||||||
|
|
||||||
// Insert all of our obtained values to the database..
|
// Insert all of our obtained values to the database..
|
||||||
_, err = registerUser.Exec(e.DeviceId(), doublyHashedDeviceToken, accountId, e.Region(), e.Country(), e.Language(), serialNo, deviceCode)
|
_, err = registerUser.Exec(e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.Region(), e.Country(), e.Language(), serialNo, deviceCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// It's okay if this isn't a MySQL error, as perhaps other issues have come in.
|
// It's okay if this isn't a MySQL error, as perhaps other issues have come in.
|
||||||
if driverErr, ok := err.(*mysql.MySQLError); ok {
|
if driverErr, ok := err.(*mysql.MySQLError); ok {
|
||||||
|
6
route.go
6
route.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -172,11 +171,8 @@ func checkAuthentication(e *Envelope) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Wii sends us a MD5 of its given token. We store a SHA-256 hash of that.
|
|
||||||
hashedDeviceToken := fmt.Sprintf("%x", sha256.Sum256([]byte(hash)))
|
|
||||||
|
|
||||||
// Check using various input given.
|
// Check using various input given.
|
||||||
row := routeVerify.QueryRow(hashedDeviceToken, accountId, e.DeviceId())
|
row := routeVerify.QueryRow(hash, accountId, e.DeviceId())
|
||||||
|
|
||||||
var throwaway string
|
var throwaway string
|
||||||
err = row.Scan(&throwaway)
|
err = row.Scan(&throwaway)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user