Guide to Building Secure APIs, the Right Way

This article is written with three categories of people in mind: junior or entry-level software developers, security testers, and anyone who designs API services. This article focuses on building web APIs to have more secure APIs.

Software development is sensitive; writing code and building web applications are insufficient. In 2021, the Cyberedge Report estimated that 86% of organizations in the research had experienced at least one successful cyberattack in 2020. That is roughly about 4 out of 5 companies!

These attacks are successful because new software developers pay little attention to security and more to building the web service itself. As a result, two kinds of threats are imminent:

  1. Threat to the developer’s software
  2. Threat to the users enjoying the software.

This article focuses on how to build secure Web services. The reader will learn to test and secure their APIs before pushing them out for consumption.

What Is An API?

Fundamentally, web service refers to a software service or functionality provided by a computer to another computer. These computers communicate using standardized communication protocols through the World Wide Web.

API stands for Application Programming Interface and is an interface that connects two computers or software. For example, a server creates an API to allow a client to access it.

Web service APIs that follow the REST architecture style is known as RESTful APIs. When you build RESTful APIs, you use XML and JSON as the data format for exchanging data among client applications and API servers.

API Basics

This section introduces the basics of APIs

API Architectures

APIs are built using different formats or architectural patterns based on the needed functionality, and the two most important ones are:

  • SOAP
  • REST

SOAP (fully known as Simple Object Access Protocol) is a web service architecture pattern that has been overtaken by REST in recent years. According to Stormpath, RESTful web services dominate the software industry, representing more than 70% of public APIs. They operate by exposing a consistent interface to access known resources.

However, SOAP exposes application logic components as services rather than data. SOAP, now a legacy architectural pattern, was created by Microsoft and had many other constraints compared to REST. For example, SOAP exchanges data solely over XML, but REST allows developers to exchange data over various formats. RESTful services are, in comparison, faster and less resource-intensive. However, SOAP still has its own use cases in which it’s a preferred protocol over REST.

SOAP is preferred when robust security is essential as it supports Web Services Security. It is, therefore, used often in sensitive environments such as banking or situations where sensitive information changes must be accessed across many resources.

API Types of API

There are a total of four types of APIs:

1. Open APIs

Open APIs require no authentication to access the API or its resources. It is open to public consumption.

2. Internal APIs

Internal APIs require authentication and are customized for a specific use case. For example, a bank app uses a series of internal APIs (likely SOAP) to allow interconnectivity across its internal infrastructure.

3. Partner APIs

Partner APIs are used when an organization extends its resources to a third party. This may be a result of a business agreement (think SaaS, etc.). This should not be confused with an internal API because internal APIs are strictly in-house. Whereas a Partner API is strictly between a client and a provider.

4. Composite APIs

They are a mix of different APIs that help make handling APIs more efficient. Composite APIs are considered an architectural solution to some efficiency problems REST APIs pose.

Building APIs, You Can Test

To practice testing your APIs, there must be an API in the first place. To be on the safe side, it is expedient for you to build your API or get legal backing for whichever API you wish to pen-test. In this section, an API is built using the Go programming language, after which it will be tested using Postman.

The Go programming language

Google’s Go programming language is a promising language for efficient web development. The Go ecosystem can be downloaded and installed locally from here.

Postman API Testing Platform

Postman is an API testing and building platform popular among software engineers, security engineers, and software documentation writers. This article uses Postman to test and secure the Go API built for explanation. You should download Postman if you do not already have it on your machine.

After installation,

Building The API

The API will rely on two major frameworks: Gin for routing and Redis for data storage.

The code is available below, with explanatory comments where necessary:

The API is a basic URL shortener. It will accept a long URL and give the user a short and memory URL back. It is based on a Base62 string mapping algorithm hosted on a simple web server that redirects the shortened URL to the original URL.

The URL shortener API will apply Base62 encoding on the long URL, generate a short URL, and store it in a database. Then, it will map the newly created short URL to the original URL. Therefore, whenever a request comes for the short URL, an HTTP redirect will forward the request to the original URL.

Installations

Use the following lines of commands to install Gin and Redis:

$ go get -u github.com/go-redis/redis/v8
$ go get -u github.com/gin-gonic/gin
$ go get github.com/itchyny/base58-go/cmd/base58

After this, building the URL shortener API comes next. Create a folder named storage and a ‘store.go’ file in it. Next, set up the wrapper to persist data:

package storage

import (
    “context”
    “fmt”
    “github.com/go-redis/redis”
    “time”
)

type Storage struct {
    redisClient *redis.Client
}

var (
    store = &Storage{}
    contx = context.Background()
)

const CacheDuration = 6 * time.Hour

func initStore() *Storage {
    redisClient := redis.NewClient(&redis.Options{
        Addr:     "localhost:8080",
        Password:” “,
        DB:       0,
    })
    pinger, err := redisClient.Ping(contx).Result()
    if err != nil {
        panic(fmt.Sprintf("Error initializing Redis: %v", err))
    }
    fmt.Printf("\nRedis initialized successfully. Pinging {%s}", pinger)
    store.redisClient = redisClient
    return store
}

func StoreUrl(shortURL string, originalURL string, userID string) {
    err := store.redisClient.Set(contx, shortURL, originalURL, CacheDuration).Err()
    if err != nil {
        panic(fmt.Sprintf("Url key not saved. Error: %v - shortURL: %s - originalURL: %s\n", err, shortURL, originalURL))
    }
}

func ReceiveUrl(shortURL string) string {
    result, err := store.redisClient.Get(contx, shortURL).Result()
    if err != nil {
        panic(fmt.Sprintf("Url key  not retrieved. Error: %v - shortURL: %s", err, shortURL))
    }
    return result
}

Now, create a folder named shortener and create a file named shorten.go to write the shortener algorithm found below:

package shortener

import (
    "crypto/sha256"
    “fmt”
    “github.com/itchyny/base58-go”
    “os”
)

// hashThis receives string and return a slice of bytes
func hashThis(inputStr string) []byte {
    shortenAlgorithm := sha256.New()
    shortenAlgorithm.Write([]byte(inputStr))
    return shortenAlgorithm.Sum(nil)
}

// Encoder encodes byte and return url string
func Encoder(bytes []byte) string {
    encoding := base58.BitcoinEncoding
    encoded, err := encoding.Encode(bytes)
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    return string(encoded)
}

func GenerateShortLink(initialLink string, userId string) string {
    urlHashBytes hashThis(initialLink + userId)
    generatedNumber := new(big.Int).SetBytes(urlHashBytes).Uint64()
    finalString := base58Encoded([]byte(fmt.Sprintf("%d", generatedNumber)))
    return finalString[:8]
}

Next, create a server folder and create a server.go file with the following code in it:

package server

import “github.com/gin-gonic/gin”

func ShortenURL(c *gin.Context) {

}

func RedirectToOriginalUrl() {

}

The server is set but will require a main.go file to run. Create a ‘main.go’ file and input the following code:

package main

import (
    “fmt”
    “github.com/freeCodeCamp-Samples/GoAPI/server”
    “github.com/freeCodeCamp-Samples/GoAPI/storage”
    “github.com/gin-gonic/gin”
)

func main() {
    // create a gin server
    serv := gin.Default()
    // specify the path
    serv.GET("/", func(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            “message”: “This is a URL Shortener API: We will shorten your URL for you.”,
        })
        serv.POST("/shorten-url", func(ctx *gin.Context) {
            server.ShortenURL(ctx)
        })
        serv.GET("/:shortUrl", func(ctx *gin.Context) {
            server.RedirectToOriginalUrl()
        })
        storage.InitStore()
    })
    // set the server to a port
    err := serv.Run(":8080")
    if err != nil {
        panic(fmt.Sprintf("Server failed... \nError: %v", err))
    }
}

Run the following command to start the server:

$ go run main.go

We will test the API by deploying it on Heroku. You can follow the Getting Started With Go on Heroku guide to host the API on Heroku.

Conclusion

This article walked you through the process of building a basic URL shortener in the Go programming language. In the article, you learned about APIs and their architecture. Furthermore, you learned the best use cases for the different types of API.

You also saw the structure of a basic Go language project and how to connect an API to a database. Finally, you created a server for the API and

In the follow-up article, you will learn how to test the API built in this article, after which you can build really secure APIs.

MacBobby Chibuzor is a Robotics Hardware Engineer and a Tech Polyglot. He also has practical experience in Software Engineering and Machine Learning, with an interest in embedded systems and Blockchain technology. In addition, Mac loves to spend his free time in technical writing.

Need help?

Let us know about your question or problem and we will reach out to you.