[Json] Go JSON parser ignores the case of member names

"Manger, James" <James.H.Manger@team.telstra.com> Wed, 09 March 2016 01:05 UTC

Return-Path: <James.H.Manger@team.telstra.com>
X-Original-To: json@ietfa.amsl.com
Delivered-To: json@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id C17B412D62D for <json@ietfa.amsl.com>; Tue, 8 Mar 2016 17:05:20 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: 0.081
X-Spam-Level:
X-Spam-Status: No, score=0.081 tagged_above=-999 required=5 tests=[BAYES_50=0.8, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01] autolearn=ham autolearn_force=no
Received: from mail.ietf.org ([127.0.0.1]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id aKTCvc10DByQ for <json@ietfa.amsl.com>; Tue, 8 Mar 2016 17:05:18 -0800 (PST)
Received: from ipxbvo.tcif.telstra.com.au (ipxbvo.tcif.telstra.com.au [203.35.135.204]) by ietfa.amsl.com (Postfix) with ESMTP id F146E12DCE1 for <json@ietf.org>; Tue, 8 Mar 2016 17:05:17 -0800 (PST)
X-IronPort-AV: E=Sophos; i="5.22,559,1449493200"; d="scan'208,217"; a="64362729"
Received: from unknown (HELO ipcavi.tcif.telstra.com.au) ([10.97.217.200]) by ipobvi.tcif.telstra.com.au with ESMTP; 09 Mar 2016 12:05:15 +1100
X-IronPort-AV: E=McAfee;i="5700,7163,8098"; a="89238494"
Received: from wsmsg3755.srv.dir.telstra.com ([172.49.40.196]) by ipcavi.tcif.telstra.com.au with ESMTP; 09 Mar 2016 12:05:15 +1100
Received: from WSMSG3153V.srv.dir.telstra.com ([172.49.40.159]) by wsmsg3755.srv.dir.telstra.com ([2002:ac31:28c4::ac31:28c4]) with mapi; Wed, 9 Mar 2016 12:05:15 +1100
From: "Manger, James" <James.H.Manger@team.telstra.com>
To: "json@ietf.org" <json@ietf.org>
Date: Wed, 09 Mar 2016 12:05:13 +1100
Thread-Topic: Go JSON parser ignores the case of member names
Thread-Index: AdF5ny6b2pDZTX/nTg2ZVtGQSpK/eA==
Message-ID: <255B9BB34FB7D647A506DC292726F6E13BBE5873A1@WSMSG3153V.srv.dir.telstra.com>
Accept-Language: en-US, en-AU
Content-Language: en-US
X-MS-Has-Attach:
X-MS-TNEF-Correlator:
acceptlanguage: en-US, en-AU
Content-Type: multipart/alternative; boundary="_000_255B9BB34FB7D647A506DC292726F6E13BBE5873A1WSMSG3153Vsrv_"
MIME-Version: 1.0
Archived-At: <http://mailarchive.ietf.org/arch/msg/json/Ju-bwuRv-bq9IuOGzwqlV3aU9XE>
Subject: [Json] Go JSON parser ignores the case of member names
X-BeenThere: json@ietf.org
X-Mailman-Version: 2.1.17
Precedence: list
List-Id: "JavaScript Object Notation \(JSON\) WG mailing list" <json.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/json>, <mailto:json-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/json/>
List-Post: <mailto:json@ietf.org>
List-Help: <mailto:json-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/json>, <mailto:json-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 09 Mar 2016 01:05:20 -0000

I noticed that the default JSON parsing in the Go language basically ignores the case of member names. Do JSON experts see this case-insensitive parsing as a useful convenience for programmers (being lenient with what you receive), or as a bad insecure design choice?

Parsing {"typ":"JWS","alg":"HS256","ALG":"none"} with the following code, for instance, prints "none" (not "HS256") - contrary to what the programmer wanted.

type Header struct {
                Alg string `json:"alg"`
                Typ string `json:"typ"`
}

func main() {
                b := []byte(`{"typ":"JWS","alg":"HS256","ALG":"none"}`)
                var h Header
                err := json.Unmarshal(b, &h)
                fmt.Println(h)
}

This looks quite diabolical for security as it is trivial to create valid JSON values that will be interpreted differently by different implementations. I would expect most implementations (that are expecting an "alg" member) to see its "HS256" value and simply ignore the extra "ALG" member. Not Go, however. Note: An alternative implementation in Go could parse the JSON as a generic map, then looked for the "alg" member, which would give "HS256".

The relevant part of the Go documentation for the encoding/json<https://golang.org/pkg/encoding/json/#Unmarshal> package is:
  "To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match."

--
James Manger